@hbslovely/baby-feeding 2026.9.2 → 2026.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, Injectable, signal, input, output, computed, ChangeDetectionStrategy, Component, DestroyRef, HostListener, effect, ViewEncapsulation, ViewChild, EventEmitter, Output, ChangeDetectorRef, Input, ViewChildren } from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, signal, input, output, computed, ChangeDetectionStrategy, Component, DestroyRef, HostListener, effect, ViewEncapsulation, ViewChild, forwardRef, EventEmitter, Output, Pipe, ChangeDetectorRef, Input, ViewChildren } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import * as i1$1 from '@angular/common';
5
5
  import { CommonModule, DecimalPipe, NgClass, NgTemplateOutlet } from '@angular/common';
@@ -8,7 +8,7 @@ import { switchMap, catchError, map, tap, shareReplay, mergeMap, scan, takeLast,
8
8
  import { throwError, of, from, Subject, interval, firstValueFrom, finalize as finalize$1, catchError as catchError$1, switchMap as switchMap$1, forkJoin, timeout } from 'rxjs';
9
9
  import { HttpClient, HttpHeaders } from '@angular/common/http';
10
10
  import * as i1 from '@angular/forms';
11
- import { FormsModule } from '@angular/forms';
11
+ import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
12
12
  import { DomSanitizer } from '@angular/platform-browser';
13
13
 
14
14
  /** Safe defaults when the host app does not provide FEEDING_CONFIG_TOKEN. */
@@ -57,9 +57,8 @@ const FEEDING_SHEET_RANGES = {
57
57
  notification: 'Notification!A2:D',
58
58
  event: 'Event!A2:G',
59
59
  medicalHistory: 'MedicalHistory!A2:H',
60
- preparingProducts: 'Settings!H2:M',
61
- /** Legacy catalog trên tab Preparing (trước khi chuyển sang Settings H:M). */
62
- preparingProductsLegacy: 'Preparing!A2:C',
60
+ /** Danh mục món cần mua (tab Preparing — tách khỏi going-out Settings H:M). */
61
+ preparingProducts: 'Preparing!A2:C',
63
62
  preparingRequests: 'Preparing!E2:M',
64
63
  preparingComments: 'Preparing!O2:T',
65
64
  };
@@ -77,7 +76,6 @@ const FEEDING_SHEET_TIER1_KEYS = [
77
76
  /** Loaded when opening settings menu (lazy batchGet). */
78
77
  const FEEDING_SHEET_PREPARING_KEYS = [
79
78
  'preparingProducts',
80
- 'preparingProductsLegacy',
81
79
  'preparingRequests',
82
80
  'preparingComments',
83
81
  ];
@@ -8513,7 +8511,7 @@ function gapHuman(min) {
8513
8511
  return `${h} giờ`;
8514
8512
  return `${h} giờ ${m} phút`;
8515
8513
  }
8516
- function escapeHtml(s) {
8514
+ function escapeHtml$1(s) {
8517
8515
  return s
8518
8516
  .replace(/&/g, '&')
8519
8517
  .replace(/</g, '&lt;')
@@ -8522,8 +8520,8 @@ function escapeHtml(s) {
8522
8520
  }
8523
8521
  function buildDayChipsHtml(e) {
8524
8522
  return e.dayChips
8525
- .map((day) => `<div class="pred-day-chip${day.hasSimilar ? ' pred-day-chip--similar' : ''}${day.feedCount === 0 ? ' pred-day-chip--empty' : ''}" title="${escapeHtml(day.dateLabel)} · ${day.feedCount} cữ">` +
8526
- `<span class="pred-day-chip-date">${escapeHtml(day.dateLabel)}</span>` +
8523
+ .map((day) => `<div class="pred-day-chip${day.hasSimilar ? ' pred-day-chip--similar' : ''}${day.feedCount === 0 ? ' pred-day-chip--empty' : ''}" title="${escapeHtml$1(day.dateLabel)} · ${day.feedCount} cữ">` +
8524
+ `<span class="pred-day-chip-date">${escapeHtml$1(day.dateLabel)}</span>` +
8527
8525
  `<span class="pred-day-chip-count">${day.feedCount}</span></div>`)
8528
8526
  .join('');
8529
8527
  }
@@ -8532,11 +8530,11 @@ function buildSimilarPairsHtml(e) {
8532
8530
  return '';
8533
8531
  const items = e.similarPairs
8534
8532
  .map((pair) => `<li class="pred-pair-item">` +
8535
- `<div class="pred-pair-when"><span class="pred-pair-date">${escapeHtml(pair.dateLabel)}</span>` +
8536
- `<span class="pred-pair-time">${escapeHtml(pair.prevTime)}</span></div>` +
8533
+ `<div class="pred-pair-when"><span class="pred-pair-date">${escapeHtml$1(pair.dateLabel)}</span>` +
8534
+ `<span class="pred-pair-time">${escapeHtml$1(pair.prevTime)}</span></div>` +
8537
8535
  `<div class="pred-pair-arrow" aria-hidden="true"><span class="pred-pair-wait">${waitHuman(pair.waitMin)}</span>` +
8538
8536
  `<i class="pi pi-arrow-right"></i></div>` +
8539
- `<div class="pred-pair-next"><span class="pred-pair-time">${escapeHtml(pair.nextTime)}</span>` +
8537
+ `<div class="pred-pair-next"><span class="pred-pair-time">${escapeHtml$1(pair.nextTime)}</span>` +
8540
8538
  `<span class="pred-pair-ml">${pair.nextVolume}ml</span></div></li>`)
8541
8539
  .join('');
8542
8540
  return `<ul class="pred-pair-list">${items}</ul>`;
@@ -8546,9 +8544,9 @@ function buildWeightedPairsHtml(e) {
8546
8544
  if (!pairs.length)
8547
8545
  return '';
8548
8546
  const rows = pairs
8549
- .map((p) => `<tr><td>${escapeHtml(p.dateLabel)} ${escapeHtml(p.prevTime)}</td>` +
8547
+ .map((p) => `<tr><td>${escapeHtml$1(p.dateLabel)} ${escapeHtml$1(p.prevTime)}</td>` +
8550
8548
  `<td>${p.prevVolume}ml</td><td>${waitHuman(p.waitMin)}</td>` +
8551
- `<td>${p.weight}</td><td>${escapeHtml(p.flags.join(', ') || '—')}</td></tr>`)
8549
+ `<td>${p.weight}</td><td>${escapeHtml$1(p.flags.join(', ') || '—')}</td></tr>`)
8552
8550
  .join('');
8553
8551
  return (`<table class="pred-weight-table"><thead><tr>` +
8554
8552
  `<th>Cặp</th><th>ml</th><th>Chờ</th><th>Trọng số</th><th>Ghi chú</th>` +
@@ -8561,7 +8559,7 @@ function buildVolumeSamplesHtml(e, maxVol) {
8561
8559
  .map((sample) => {
8562
8560
  const pct = Math.max(8, Math.round((sample.volume / maxVol) * 100));
8563
8561
  return (`<div class="pred-vol-row">` +
8564
- `<span class="pred-vol-meta">${escapeHtml(sample.dateLabel)} · ${escapeHtml(sample.time)}</span>` +
8562
+ `<span class="pred-vol-meta">${escapeHtml$1(sample.dateLabel)} · ${escapeHtml$1(sample.time)}</span>` +
8565
8563
  `<div class="pred-vol-track"><div class="pred-vol-fill" style="width:${pct}%"></div></div>` +
8566
8564
  `<span class="pred-vol-ml">${sample.volume}ml</span></div>`);
8567
8565
  })
@@ -8571,7 +8569,7 @@ function buildConfidenceChecksHtml(e) {
8571
8569
  return e.confidenceChecks
8572
8570
  .map((check) => `<li class="${check.ok ? 'pred-check--ok' : 'pred-check--no'}">` +
8573
8571
  `<i class="pi ${check.ok ? 'pi-check-circle' : 'pi-times-circle'}"></i> ` +
8574
- `${escapeHtml(check.label)}</li>`)
8572
+ `${escapeHtml$1(check.label)}</li>`)
8575
8573
  .join('');
8576
8574
  }
8577
8575
  /** Xây map biến cho bindTemplate từ prediction hiện tại. */
@@ -9333,7 +9331,6 @@ class FeedingDailyComponent {
9333
9331
  { ml: 100, icon: 'fa-droplet' },
9334
9332
  { ml: 110, icon: 'fa-glass-water' },
9335
9333
  { ml: 120, icon: 'fa-mug-saucer' },
9336
- { ml: 130, icon: 'fa-bottle-droplet' },
9337
9334
  { ml: 150, icon: 'fa-bottle-water' },
9338
9335
  { ml: 160, icon: 'fa-mug-hot' },
9339
9336
  { ml: 180, icon: 'fa-fill-drip' },
@@ -10925,6 +10922,184 @@ function viFoldIncludes(haystack, needle) {
10925
10922
  return foldViKey(haystack).includes(n);
10926
10923
  }
10927
10924
 
10925
+ const ALLOWED_TAGS = new Set([
10926
+ 'B',
10927
+ 'STRONG',
10928
+ 'I',
10929
+ 'EM',
10930
+ 'U',
10931
+ 'UL',
10932
+ 'OL',
10933
+ 'LI',
10934
+ 'P',
10935
+ 'BR',
10936
+ 'DIV',
10937
+ 'SPAN',
10938
+ ]);
10939
+ /** Escape plain text for safe HTML insertion. */
10940
+ function escapeHtml(text) {
10941
+ return text
10942
+ .replace(/&/g, '&amp;')
10943
+ .replace(/</g, '&lt;')
10944
+ .replace(/>/g, '&gt;')
10945
+ .replace(/"/g, '&quot;')
10946
+ .replace(/'/g, '&#39;');
10947
+ }
10948
+ /** Strip all HTML tags → plain text (search / fallback). */
10949
+ function stripHtml(html) {
10950
+ if (!html)
10951
+ return '';
10952
+ if (typeof document === 'undefined') {
10953
+ return html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
10954
+ }
10955
+ const doc = new DOMParser().parseFromString(html, 'text/html');
10956
+ return (doc.body.textContent || '').replace(/\s+/g, ' ').trim();
10957
+ }
10958
+ function looksLikeHtml(value) {
10959
+ return /<[a-z][\s\S]*>/i.test(value);
10960
+ }
10961
+ function serializeAllowed(node) {
10962
+ if (node.nodeType === Node.TEXT_NODE) {
10963
+ return escapeHtml(node.textContent ?? '');
10964
+ }
10965
+ if (node.nodeType !== Node.ELEMENT_NODE) {
10966
+ return '';
10967
+ }
10968
+ const el = node;
10969
+ const tag = el.tagName.toUpperCase();
10970
+ if (tag === 'BR') {
10971
+ return '<br>';
10972
+ }
10973
+ const inner = Array.from(el.childNodes).map(serializeAllowed).join('');
10974
+ if (!ALLOWED_TAGS.has(tag)) {
10975
+ return inner;
10976
+ }
10977
+ const lower = tag.toLowerCase();
10978
+ if (tag === 'DIV' || tag === 'P' || tag === 'LI') {
10979
+ return `<${lower}>${inner}</${lower}>`;
10980
+ }
10981
+ return `<${lower}>${inner}</${lower}>`;
10982
+ }
10983
+ /**
10984
+ * Sanitize rich-text HTML for storage / display.
10985
+ * Plain text (legacy) is escaped and newlines become `<br>`.
10986
+ */
10987
+ function sanitizeRichHtml(value) {
10988
+ const raw = (value ?? '').trim();
10989
+ if (!raw)
10990
+ return '';
10991
+ if (!looksLikeHtml(raw)) {
10992
+ return escapeHtml(raw).replace(/\r\n|\r|\n/g, '<br>');
10993
+ }
10994
+ if (typeof document === 'undefined') {
10995
+ return escapeHtml(stripHtml(raw));
10996
+ }
10997
+ const doc = new DOMParser().parseFromString(raw, 'text/html');
10998
+ const html = Array.from(doc.body.childNodes).map(serializeAllowed).join('');
10999
+ const text = stripHtml(html);
11000
+ return text ? html : '';
11001
+ }
11002
+ /** True when editor HTML is effectively empty. */
11003
+ function isEmptyRichHtml(html) {
11004
+ return !stripHtml(html ?? '');
11005
+ }
11006
+
11007
+ class RichTextEditorComponent {
11008
+ constructor() {
11009
+ this.placeholder = input('Nhập nội dung…', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
11010
+ /** Min height of the editable area (px). */
11011
+ this.minHeight = input(160, ...(ngDevMode ? [{ debugName: "minHeight" }] : /* istanbul ignore next */ []));
11012
+ this.disabled = signal(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
11013
+ this.focused = signal(false, ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
11014
+ this.onChange = () => undefined;
11015
+ this.onTouched = () => undefined;
11016
+ this.lastEmitted = '';
11017
+ this.pendingHtml = null;
11018
+ this.viewReady = false;
11019
+ }
11020
+ ngAfterViewInit() {
11021
+ this.viewReady = true;
11022
+ this.applyPendingHtml();
11023
+ }
11024
+ writeValue(value) {
11025
+ const html = sanitizeRichHtml(value ?? '');
11026
+ this.lastEmitted = html;
11027
+ this.pendingHtml = html;
11028
+ if (this.viewReady) {
11029
+ this.applyPendingHtml();
11030
+ }
11031
+ }
11032
+ registerOnChange(fn) {
11033
+ this.onChange = fn;
11034
+ }
11035
+ registerOnTouched(fn) {
11036
+ this.onTouched = fn;
11037
+ }
11038
+ setDisabledState(isDisabled) {
11039
+ this.disabled.set(isDisabled);
11040
+ }
11041
+ onInput() {
11042
+ this.emitValue();
11043
+ }
11044
+ onFocus() {
11045
+ this.focused.set(true);
11046
+ }
11047
+ onBlur() {
11048
+ this.focused.set(false);
11049
+ this.onTouched();
11050
+ this.emitValue();
11051
+ }
11052
+ exec(command, event) {
11053
+ event.preventDefault();
11054
+ if (this.disabled())
11055
+ return;
11056
+ this.editorRef?.nativeElement.focus();
11057
+ document.execCommand(command, false);
11058
+ this.emitValue();
11059
+ }
11060
+ applyPendingHtml() {
11061
+ const el = this.editorRef?.nativeElement;
11062
+ if (!el || this.pendingHtml === null)
11063
+ return;
11064
+ if (el.innerHTML !== this.pendingHtml) {
11065
+ el.innerHTML = this.pendingHtml;
11066
+ }
11067
+ this.pendingHtml = null;
11068
+ }
11069
+ emitValue() {
11070
+ const el = this.editorRef?.nativeElement;
11071
+ if (!el)
11072
+ return;
11073
+ const raw = el.innerHTML;
11074
+ const html = isEmptyRichHtml(raw) ? '' : sanitizeRichHtml(raw);
11075
+ if (html === this.lastEmitted)
11076
+ return;
11077
+ this.lastEmitted = html;
11078
+ this.onChange(html);
11079
+ }
11080
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RichTextEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11081
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: RichTextEditorComponent, isStandalone: true, selector: "app-rich-text-editor", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
11082
+ {
11083
+ provide: NG_VALUE_ACCESSOR,
11084
+ useExisting: forwardRef(() => RichTextEditorComponent),
11085
+ multi: true,
11086
+ },
11087
+ ], viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true }], ngImport: i0, template: "<div\n class=\"rte\"\n [class.rte--focused]=\"focused()\"\n [class.rte--disabled]=\"disabled()\"\n [style.--rte-min-h.px]=\"minHeight()\"\n>\n <div class=\"rte__toolbar\" role=\"toolbar\" aria-label=\"\u0110\u1ECBnh d\u1EA1ng v\u0103n b\u1EA3n\">\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"\u0110\u1EADm\"\n aria-label=\"\u0110\u1EADm\"\n (mousedown)=\"exec('bold', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-bold\" aria-hidden=\"true\"></i>\n </button>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"Nghi\u00EAng\"\n aria-label=\"Nghi\u00EAng\"\n (mousedown)=\"exec('italic', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-italic\" aria-hidden=\"true\"></i>\n </button>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"G\u1EA1ch d\u01B0\u1EDBi\"\n aria-label=\"G\u1EA1ch d\u01B0\u1EDBi\"\n (mousedown)=\"exec('underline', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-underline\" aria-hidden=\"true\"></i>\n </button>\n <span class=\"rte__sep\" aria-hidden=\"true\"></span>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"Danh s\u00E1ch\"\n aria-label=\"Danh s\u00E1ch\"\n (mousedown)=\"exec('insertUnorderedList', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-list-ul\" aria-hidden=\"true\"></i>\n </button>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"Danh s\u00E1ch s\u1ED1\"\n aria-label=\"Danh s\u00E1ch s\u1ED1\"\n (mousedown)=\"exec('insertOrderedList', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-list-ol\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div\n #editor\n class=\"rte__editor\"\n role=\"textbox\"\n aria-multiline=\"true\"\n [attr.data-placeholder]=\"placeholder()\"\n [attr.contenteditable]=\"disabled() ? 'false' : 'true'\"\n (input)=\"onInput()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n ></div>\n</div>\n", styles: [".rte{display:flex;flex-direction:column;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);overflow:hidden}.rte--focused{border-color:var(--primary, #5b6cff);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary, #5b6cff) 22%,transparent)}.rte--disabled{opacity:.65;pointer-events:none}.rte__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:6px 8px;border-bottom:1px solid var(--border, #e5e7eb);background:var(--surface-2, #f8fafc)}.rte__btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:none;border-radius:6px;background:transparent;color:var(--text, #0f172a);cursor:pointer;font-size:13px}.rte__btn:hover:not(:disabled){background:color-mix(in srgb,var(--primary, #5b6cff) 12%,white)}.rte__btn:disabled{opacity:.45;cursor:not-allowed}.rte__sep{width:1px;height:18px;margin:0 4px;background:var(--border, #e5e7eb)}.rte__editor{min-height:var(--rte-min-h, 160px);max-height:min(42vh,320px);overflow-y:auto;padding:10px 12px;font-size:15px;line-height:1.5;font-family:inherit;color:var(--text, #0f172a);outline:none;word-break:break-word}.rte__editor:empty:before{content:attr(data-placeholder);color:var(--muted-2, #94a3b8);pointer-events:none}.rte__editor ul,.rte__editor ol{margin:.35em 0;padding-left:1.35em}.rte__editor p{margin:0 0 .4em}.rte__editor p:last-child{margin-bottom:0}.rte__editor b,.rte__editor strong{font-weight:700}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11088
+ }
11089
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: RichTextEditorComponent, decorators: [{
11090
+ type: Component,
11091
+ args: [{ selector: 'app-rich-text-editor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
11092
+ {
11093
+ provide: NG_VALUE_ACCESSOR,
11094
+ useExisting: forwardRef(() => RichTextEditorComponent),
11095
+ multi: true,
11096
+ },
11097
+ ], template: "<div\n class=\"rte\"\n [class.rte--focused]=\"focused()\"\n [class.rte--disabled]=\"disabled()\"\n [style.--rte-min-h.px]=\"minHeight()\"\n>\n <div class=\"rte__toolbar\" role=\"toolbar\" aria-label=\"\u0110\u1ECBnh d\u1EA1ng v\u0103n b\u1EA3n\">\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"\u0110\u1EADm\"\n aria-label=\"\u0110\u1EADm\"\n (mousedown)=\"exec('bold', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-bold\" aria-hidden=\"true\"></i>\n </button>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"Nghi\u00EAng\"\n aria-label=\"Nghi\u00EAng\"\n (mousedown)=\"exec('italic', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-italic\" aria-hidden=\"true\"></i>\n </button>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"G\u1EA1ch d\u01B0\u1EDBi\"\n aria-label=\"G\u1EA1ch d\u01B0\u1EDBi\"\n (mousedown)=\"exec('underline', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-underline\" aria-hidden=\"true\"></i>\n </button>\n <span class=\"rte__sep\" aria-hidden=\"true\"></span>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"Danh s\u00E1ch\"\n aria-label=\"Danh s\u00E1ch\"\n (mousedown)=\"exec('insertUnorderedList', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-list-ul\" aria-hidden=\"true\"></i>\n </button>\n <button\n type=\"button\"\n class=\"rte__btn\"\n title=\"Danh s\u00E1ch s\u1ED1\"\n aria-label=\"Danh s\u00E1ch s\u1ED1\"\n (mousedown)=\"exec('insertOrderedList', $event)\"\n [disabled]=\"disabled()\"\n >\n <i class=\"fa-solid fa-list-ol\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div\n #editor\n class=\"rte__editor\"\n role=\"textbox\"\n aria-multiline=\"true\"\n [attr.data-placeholder]=\"placeholder()\"\n [attr.contenteditable]=\"disabled() ? 'false' : 'true'\"\n (input)=\"onInput()\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n ></div>\n</div>\n", styles: [".rte{display:flex;flex-direction:column;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);overflow:hidden}.rte--focused{border-color:var(--primary, #5b6cff);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary, #5b6cff) 22%,transparent)}.rte--disabled{opacity:.65;pointer-events:none}.rte__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:6px 8px;border-bottom:1px solid var(--border, #e5e7eb);background:var(--surface-2, #f8fafc)}.rte__btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:none;border-radius:6px;background:transparent;color:var(--text, #0f172a);cursor:pointer;font-size:13px}.rte__btn:hover:not(:disabled){background:color-mix(in srgb,var(--primary, #5b6cff) 12%,white)}.rte__btn:disabled{opacity:.45;cursor:not-allowed}.rte__sep{width:1px;height:18px;margin:0 4px;background:var(--border, #e5e7eb)}.rte__editor{min-height:var(--rte-min-h, 160px);max-height:min(42vh,320px);overflow-y:auto;padding:10px 12px;font-size:15px;line-height:1.5;font-family:inherit;color:var(--text, #0f172a);outline:none;word-break:break-word}.rte__editor:empty:before{content:attr(data-placeholder);color:var(--muted-2, #94a3b8);pointer-events:none}.rte__editor ul,.rte__editor ol{margin:.35em 0;padding-left:1.35em}.rte__editor p{margin:0 0 .4em}.rte__editor p:last-child{margin-bottom:0}.rte__editor b,.rte__editor strong{font-weight:700}\n"] }]
11098
+ }], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], editorRef: [{
11099
+ type: ViewChild,
11100
+ args: ['editor']
11101
+ }] } });
11102
+
10928
11103
  /**
10929
11104
  * Gợi ý nơi khám — có thể chọn nhanh hoặc gõ thêm (lưu cục bộ theo user).
10930
11105
  */
@@ -11197,7 +11372,7 @@ class MedicalHistoryDialogComponent {
11197
11372
  date: d.date,
11198
11373
  kind: d.kind,
11199
11374
  title,
11200
- detail: d.detail.trim(),
11375
+ detail: sanitizeRichHtml(d.detail),
11201
11376
  place: d.place.trim() || undefined,
11202
11377
  driveFileId,
11203
11378
  };
@@ -11384,11 +11559,11 @@ class MedicalHistoryDialogComponent {
11384
11559
  return Math.max(0, Math.floor((payload.length * 3) / 4) - padding);
11385
11560
  }
11386
11561
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: MedicalHistoryDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11387
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: MedicalHistoryDialogComponent, isStandalone: true, selector: "app-medical-history-dialog", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, explorerEntries: { classPropertyName: "explorerEntries", publicName: "explorerEntries", isSignal: true, isRequired: false, transformFunction: null }, medicalFolderId: { classPropertyName: "medicalFolderId", publicName: "medicalFolderId", isSignal: true, isRequired: false, transformFunction: null }, entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", saved: "saved" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "@if (dialogOpen()) {\n <div class=\"dialog-overlay\" (click)=\"closeDialog()\">\n <div\n class=\"dialog medical-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head\">\n <div>\n <h2>\n <i class=\"pi pi-pencil\"></i>\n {{ editingEntry() ? 'S\u1EEDa s\u1EF1 ki\u1EC7n' : 'Th\u00EAm s\u1EF1 ki\u1EC7n' }}\n </h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n @if (errorMsg()) {\n <div class=\"alert error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n {{ errorMsg() }}\n </div>\n }\n <form class=\"medical-form\" (ngSubmit)=\"submitDialog()\">\n <label class=\"field\">\n <span class=\"label\">Ng\u00E0y</span>\n <input\n type=\"date\"\n [ngModel]=\"draft().date\"\n (ngModelChange)=\"updateDraft({ date: $event })\"\n name=\"mhDate\"\n [max]=\"maxDateToday\"\n required\n />\n </label>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">Lo\u1EA1i</span>\n <div class=\"mh-combo\" [class.open]=\"kindPickerOpen()\">\n <button\n type=\"button\"\n class=\"mh-combo-trigger\"\n (click)=\"toggleKindPicker(); $event.stopPropagation()\"\n >\n <span class=\"mh-combo-trigger__icon\"\n ><i class=\"fa-solid\" [ngClass]=\"metaFor(draft().kind).icon\"></i\n ></span>\n <span class=\"mh-combo-trigger__text\">{{ metaFor(draft().kind).label }}</span>\n <i class=\"pi pi-chevron-down mh-combo-chev\"></i>\n </button>\n @if (kindPickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"search\"\n class=\"mh-combo-search\"\n placeholder=\"T\u00ECm lo\u1EA1i\u2026\"\n [ngModel]=\"kindSearch()\"\n (ngModelChange)=\"kindSearch.set($event)\"\n name=\"mhKindSearch\"\n autocomplete=\"off\"\n (click)=\"$event.stopPropagation()\"\n />\n <ul class=\"mh-combo-list\" role=\"listbox\">\n @for (km of filteredKindCatalog(); track km) {\n <li\n role=\"option\"\n [class.active]=\"draft().kind === km.id\"\n (click)=\"selectKind(km.id)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"km.icon\"></i> {{ km.label }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <label class=\"field\">\n <span class=\"label\">Ti\u00EAu \u0111\u1EC1</span>\n <input\n type=\"text\"\n [ngModel]=\"draft().title\"\n (ngModelChange)=\"updateDraft({ title: $event })\"\n name=\"mhTitle\"\n placeholder=\"VD: M\u0169i 2 Pentaxim \u2014 BV Nhi\"\n required\n />\n </label>\n <label class=\"field\">\n <span class=\"label\">Chi ti\u1EBFt</span>\n <textarea\n [ngModel]=\"draft().detail\"\n (ngModelChange)=\"updateDraft({ detail: $event })\"\n name=\"mhDetail\"\n rows=\"3\"\n placeholder=\"Ph\u1EA3n \u1EE9ng, ch\u1EC9 \u0111\u1ECBnh BS, li\u1EC1u\u2026\"\n ></textarea>\n </label>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">N\u01A1i kh\u00E1m</span>\n <div class=\"mh-combo mh-place-combo\" [class.open]=\"placePickerOpen()\">\n <input\n type=\"text\"\n class=\"mh-combo-input\"\n [ngModel]=\"draft().place\"\n (ngModelChange)=\"\n updateDraft({ place: $event }); kindPickerOpen.set(false); placePickerOpen.set(true)\n \"\n (focus)=\"onPlaceFieldFocus()\"\n name=\"mhPlace\"\n placeholder=\"Ch\u1ECDn ho\u1EB7c g\u00F5 t\u00EAn c\u01A1 s\u1EDF\u2026\"\n autocomplete=\"off\"\n />\n @if (placePickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n @if (canAddCustomPlace()) {\n <button type=\"button\" class=\"mh-combo-add\" (click)=\"addCustomPlaceFromDraft()\">\n <i class=\"pi pi-plus\"></i> Th\u00EAm \u00AB{{ draft().place.trim() }}\u00BB v\u00E0o danh s\u00E1ch\n </button>\n }\n <ul class=\"mh-combo-list mh-combo-list--places\">\n @for (p of filteredPlaceSuggestions(); track p) {\n <li (click)=\"selectPlace(p)\">\n {{ p }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <div class=\"field med-attach-field\">\n <span class=\"label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</span>\n @if (\n editingEntry() &&\n !pendingAttachmentPreviewUrl() &&\n !stripExistingAttachment() &&\n attachmentFor(editingEntry()!);\n as sav\n ) {\n <div class=\"med-preview med-preview--saved\">\n @if (sav.content || sav.previewUrl) {\n <img [src]=\"sav.previewUrl || sav.content\" alt=\"\u0110\u00EDnh k\u00E8m hi\u1EC7n t\u1EA1i\" />\n }\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"removeSavedAttachment()\">\n <i class=\"pi pi-times\"></i> G\u1EE1 \u1EA3nh \u0111\u00E3 l\u01B0u\n </button>\n </div>\n }\n @if (pendingAttachmentPreviewUrl(); as prevUrl) {\n <div class=\"med-preview med-preview--new\">\n <img [src]=\"prevUrl\" alt=\"Xem tr\u01B0\u1EDBc\" />\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"clearPendingAttachment()\">\n Hu\u1EF7 \u1EA3nh m\u1EDBi\n </button>\n </div>\n }\n <label class=\"med-upload-card\">\n <input\n type=\"file\"\n accept=\"image/*,.heic,.heif\"\n class=\"sr-only\"\n (change)=\"onAttachmentInput($any($event.target))\"\n />\n <span class=\"med-upload-card__icon\" aria-hidden=\"true\">\n <i class=\"pi pi-images\"></i>\n </span>\n <span class=\"med-upload-card__text\">\n <strong class=\"med-upload-card__title\">{{\n pendingAttachment() ? '\u0110\u1ED5i \u1EA3nh' : 'Ch\u1ECDn \u1EA3nh'\n }}</strong>\n </span>\n <span class=\"med-upload-card__arrow\" aria-hidden=\"true\">\n <i class=\"pi pi-angle-right\"></i>\n </span>\n </label>\n </div>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeDialog()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"submit\" class=\"btn primary\" [disabled]=\"saving()\">\n @if (!saving()) {\n <i class=\"pi pi-check\"></i>\n }\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\"></i>\n }\n {{ editingEntry() ? 'C\u1EADp nh\u1EADt' : 'L\u01B0u' }}\n </button>\n </div>\n </form>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.medical-dialog{width:100%;max-width:480px;max-height:90vh;overflow:auto;background:var(--surface, #fff);border-radius:0;padding:0 0 12px;box-shadow:0 24px 48px -24px #0f172a59}@media(min-width:640px){.medical-dialog{max-height:min(88dvh,720px);padding-bottom:env(safe-area-inset-bottom,12px)}}.medical-dialog .field input,.medical-dialog .field select,.medical-dialog .field textarea,.medical-dialog .mh-combo-trigger,.medical-dialog .mh-combo-input{padding:8px 10px;border-radius:0}.medical-dialog .mh-combo-panel,.medical-dialog .mh-combo-search,.medical-dialog .med-upload-card{border-radius:0}.medical-dialog .alert.error{display:flex;align-items:flex-start;gap:10px;margin:0 16px 8px;padding:10px 12px;border-radius:12px;font-size:13px;background:#fef2f2;color:#b91c1c;border:1px solid #fecaca}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:14px 16px 10px;border-bottom:1px solid var(--border, #e5e7eb)}.dialog-head h2{margin:0;font-size:1.1rem;font-weight:800;display:flex;align-items:center;gap:8px}.dialog-close{border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px}.dialog-close:hover{background:var(--surface-2, #f1f5f9)}.medical-form{padding:12px 16px 16px;display:flex;flex-direction:column;gap:10px}.field{display:flex;flex-direction:column;gap:4px}.field .label{font-size:12px;font-weight:700;color:var(--muted)}.field input,.field select,.field textarea{padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.field textarea{resize:vertical;min-height:72px}.dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px;padding-top:4px}.btn.ghost{border:1px solid var(--border);background:transparent;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.ghost.sm{padding:6px 12px;font-size:12px;border-radius:10px}.btn.primary{background:var(--primary, #5b6cff);color:#fff;border:none;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.primary:disabled{opacity:.65;cursor:not-allowed}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.mh-combo-field{position:relative;z-index:1}.medical-form .mh-combo-field:has(.mh-combo.open){z-index:50}.mh-combo{position:relative;z-index:2}.mh-combo.open{z-index:10}.mh-combo-trigger{width:100%;display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff);cursor:pointer;text-align:left}.mh-combo-trigger__icon{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.mh-combo-trigger__icon i{font-size:14px}.mh-combo-trigger__text{flex:1;min-width:0;font-weight:600;color:var(--text)}.mh-combo-chev{font-size:12px;color:var(--muted);transition:transform .15s}.mh-combo.open .mh-combo-chev{transform:rotate(-180deg)}.mh-combo-input{width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.mh-combo-panel{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:20;border-radius:12px;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);box-shadow:0 12px 28px -12px #0f172a40;overflow:hidden}.mh-combo-search{width:100%;border:none;border-bottom:1px solid var(--border, #e5e7eb);padding:10px 12px;font-size:14px;font-family:inherit;outline:none;background:var(--surface-2, #f8fafc)}.mh-combo-list{list-style:none;margin:0;padding:6px;max-height:220px;overflow-y:auto}.mh-combo-list li{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:8px;cursor:pointer;font-size:14px;line-height:1.3}.mh-combo-list li i{color:var(--muted);flex-shrink:0}.mh-combo-list li:hover{background:var(--surface-2, #f1f5f9)}.mh-combo-list li.active{background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);font-weight:600}.mh-combo-list--places li{font-weight:500}.mh-combo-add{display:flex;align-items:center;gap:8px;width:calc(100% - 12px);margin:6px;padding:10px 12px;border-radius:10px;border:1px dashed var(--border, #cbd5e1);background:color-mix(in srgb,var(--primary, #5b6cff) 8%,white);color:var(--primary, #5b6cff);font-size:13px;font-weight:700;cursor:pointer;font-family:inherit}.mh-combo-add:hover{filter:brightness(.97)}.med-attach-field{margin-top:0}.med-preview{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px}.med-preview img{max-width:100%;max-height:120px;border-radius:12px;border:1px solid var(--border, #e5e7eb);object-fit:contain}.med-upload-card{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;margin:0;border-radius:14px;border:1px solid var(--border, #e5e7eb);background:linear-gradient(135deg,color-mix(in srgb,var(--primary, #5b6cff) 6%,white),var(--surface, #fff));cursor:pointer;font-family:inherit;box-sizing:border-box;transition:border-color .15s,box-shadow .15s}.med-upload-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 45%,#e5e7eb);box-shadow:0 6px 20px -12px #5b6cff59}.med-upload-card:active{transform:scale(.99)}.med-upload-card:focus-within{outline:none;border-color:var(--primary, #5b6cff);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary, #5b6cff) 24%,transparent)}.med-upload-card__icon{flex-shrink:0;width:38px;height:38px;display:flex;align-items:center;justify-content:center;border-radius:12px;background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);color:var(--primary, #5b6cff);font-size:18px}.med-upload-card__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left}.med-upload-card__title{font-size:14px;font-weight:800;color:var(--text);line-height:1.25}.med-upload-card__arrow{flex-shrink:0;color:var(--muted);font-size:14px;opacity:.85}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11562
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: MedicalHistoryDialogComponent, isStandalone: true, selector: "app-medical-history-dialog", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, explorerEntries: { classPropertyName: "explorerEntries", publicName: "explorerEntries", isSignal: true, isRequired: false, transformFunction: null }, medicalFolderId: { classPropertyName: "medicalFolderId", publicName: "medicalFolderId", isSignal: true, isRequired: false, transformFunction: null }, entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", saved: "saved" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "@if (dialogOpen()) {\n <div class=\"dialog-overlay\" (click)=\"closeDialog()\">\n <div\n class=\"dialog medical-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head\">\n <div>\n <h2>\n <i class=\"pi pi-pencil\"></i>\n {{ editingEntry() ? 'S\u1EEDa s\u1EF1 ki\u1EC7n' : 'Th\u00EAm s\u1EF1 ki\u1EC7n' }}\n </h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n @if (errorMsg()) {\n <div class=\"alert error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n {{ errorMsg() }}\n </div>\n }\n <form class=\"medical-form\" (ngSubmit)=\"submitDialog()\">\n <div class=\"medical-form__body\">\n <label class=\"field\">\n <span class=\"label\">Ng\u00E0y</span>\n <input\n type=\"date\"\n [ngModel]=\"draft().date\"\n (ngModelChange)=\"updateDraft({ date: $event })\"\n name=\"mhDate\"\n [max]=\"maxDateToday\"\n required\n />\n </label>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">Lo\u1EA1i</span>\n <div class=\"mh-combo\" [class.open]=\"kindPickerOpen()\">\n <button\n type=\"button\"\n class=\"mh-combo-trigger\"\n (click)=\"toggleKindPicker(); $event.stopPropagation()\"\n >\n <span class=\"mh-combo-trigger__icon\"\n ><i class=\"fa-solid\" [ngClass]=\"metaFor(draft().kind).icon\"></i\n ></span>\n <span class=\"mh-combo-trigger__text\">{{ metaFor(draft().kind).label }}</span>\n <i class=\"pi pi-chevron-down mh-combo-chev\"></i>\n </button>\n @if (kindPickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"search\"\n class=\"mh-combo-search\"\n placeholder=\"T\u00ECm lo\u1EA1i\u2026\"\n [ngModel]=\"kindSearch()\"\n (ngModelChange)=\"kindSearch.set($event)\"\n name=\"mhKindSearch\"\n autocomplete=\"off\"\n (click)=\"$event.stopPropagation()\"\n />\n <ul class=\"mh-combo-list\" role=\"listbox\">\n @for (km of filteredKindCatalog(); track km) {\n <li\n role=\"option\"\n [class.active]=\"draft().kind === km.id\"\n (click)=\"selectKind(km.id)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"km.icon\"></i> {{ km.label }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <label class=\"field\">\n <span class=\"label\">Ti\u00EAu \u0111\u1EC1</span>\n <input\n type=\"text\"\n [ngModel]=\"draft().title\"\n (ngModelChange)=\"updateDraft({ title: $event })\"\n name=\"mhTitle\"\n placeholder=\"VD: M\u0169i 2 Pentaxim \u2014 BV Nhi\"\n required\n />\n </label>\n <div class=\"field field--rich\">\n <span class=\"label\">Chi ti\u1EBFt</span>\n <app-rich-text-editor\n [ngModel]=\"draft().detail\"\n (ngModelChange)=\"updateDraft({ detail: $event })\"\n name=\"mhDetail\"\n [minHeight]=\"200\"\n placeholder=\"Ph\u1EA3n \u1EE9ng, ch\u1EC9 \u0111\u1ECBnh BS, li\u1EC1u\u2026\"\n />\n </div>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">N\u01A1i kh\u00E1m</span>\n <div class=\"mh-combo mh-place-combo\" [class.open]=\"placePickerOpen()\">\n <input\n type=\"text\"\n class=\"mh-combo-input\"\n [ngModel]=\"draft().place\"\n (ngModelChange)=\"\n updateDraft({ place: $event });\n kindPickerOpen.set(false);\n placePickerOpen.set(true)\n \"\n (focus)=\"onPlaceFieldFocus()\"\n name=\"mhPlace\"\n placeholder=\"Ch\u1ECDn ho\u1EB7c g\u00F5 t\u00EAn c\u01A1 s\u1EDF\u2026\"\n autocomplete=\"off\"\n />\n @if (placePickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n @if (canAddCustomPlace()) {\n <button type=\"button\" class=\"mh-combo-add\" (click)=\"addCustomPlaceFromDraft()\">\n <i class=\"pi pi-plus\"></i> Th\u00EAm \u00AB{{ draft().place.trim() }}\u00BB v\u00E0o danh s\u00E1ch\n </button>\n }\n <ul class=\"mh-combo-list mh-combo-list--places\">\n @for (p of filteredPlaceSuggestions(); track p) {\n <li (click)=\"selectPlace(p)\">\n {{ p }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <div class=\"field med-attach-field\">\n <span class=\"label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</span>\n @if (\n editingEntry() &&\n !pendingAttachmentPreviewUrl() &&\n !stripExistingAttachment() &&\n attachmentFor(editingEntry()!);\n as sav\n ) {\n <div class=\"med-preview med-preview--saved\">\n @if (sav.content || sav.previewUrl) {\n <img [src]=\"sav.previewUrl || sav.content\" alt=\"\u0110\u00EDnh k\u00E8m hi\u1EC7n t\u1EA1i\" />\n }\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"removeSavedAttachment()\">\n <i class=\"pi pi-times\"></i> G\u1EE1 \u1EA3nh \u0111\u00E3 l\u01B0u\n </button>\n </div>\n }\n @if (pendingAttachmentPreviewUrl(); as prevUrl) {\n <div class=\"med-preview med-preview--new\">\n <img [src]=\"prevUrl\" alt=\"Xem tr\u01B0\u1EDBc\" />\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"clearPendingAttachment()\">\n Hu\u1EF7 \u1EA3nh m\u1EDBi\n </button>\n </div>\n }\n <label class=\"med-upload-card\">\n <input\n type=\"file\"\n accept=\"image/*,.heic,.heif\"\n class=\"sr-only\"\n (change)=\"onAttachmentInput($any($event.target))\"\n />\n <span class=\"med-upload-card__icon\" aria-hidden=\"true\">\n <i class=\"pi pi-images\"></i>\n </span>\n <span class=\"med-upload-card__text\">\n <strong class=\"med-upload-card__title\">{{\n pendingAttachment() ? '\u0110\u1ED5i \u1EA3nh' : 'Ch\u1ECDn \u1EA3nh'\n }}</strong>\n </span>\n <span class=\"med-upload-card__arrow\" aria-hidden=\"true\">\n <i class=\"pi pi-angle-right\"></i>\n </span>\n </label>\n </div>\n </div>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeDialog()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"submit\" class=\"btn primary\" [disabled]=\"saving()\">\n @if (!saving()) {\n <i class=\"pi pi-check\"></i>\n }\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\"></i>\n }\n {{ editingEntry() ? 'C\u1EADp nh\u1EADt' : 'L\u01B0u' }}\n </button>\n </div>\n </form>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.medical-dialog{width:100%;max-width:480px;max-height:min(90vh,720px);overflow:hidden;display:flex;flex-direction:column;background:var(--surface, #fff);border-radius:0;padding:0;box-shadow:0 24px 48px -24px #0f172a59}@media(min-width:640px){.medical-dialog{max-height:min(88dvh,720px)}}.medical-dialog .field input,.medical-dialog .field select,.medical-dialog .field textarea,.medical-dialog .mh-combo-trigger,.medical-dialog .mh-combo-input{padding:8px 10px;border-radius:0}.medical-dialog .mh-combo-panel,.medical-dialog .mh-combo-search,.medical-dialog .med-upload-card{border-radius:0}.medical-dialog .alert.error{display:flex;align-items:flex-start;gap:10px;flex-shrink:0;margin:0 16px 8px;padding:10px 12px;border-radius:12px;font-size:13px;background:#fef2f2;color:#b91c1c;border:1px solid #fecaca}.dialog-head{flex-shrink:0;display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:14px 16px 10px;border-bottom:1px solid var(--border, #e5e7eb)}.dialog-head h2{margin:0;font-size:1.1rem;font-weight:800;display:flex;align-items:center;gap:8px}.dialog-close{border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px}.dialog-close:hover{background:var(--surface-2, #f1f5f9)}.medical-form{flex:1;min-height:0;display:flex;flex-direction:column;padding:0;gap:0}.medical-form__body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:12px 16px;display:flex;flex-direction:column;gap:10px;-webkit-overflow-scrolling:touch}.field{display:flex;flex-direction:column;gap:4px}.field .label{font-size:12px;font-weight:700;color:var(--muted)}.field input,.field select,.field textarea{padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.field textarea{resize:vertical;min-height:72px}.field--rich .label{margin-bottom:2px}.dialog-actions{flex-shrink:0;display:flex;justify-content:flex-end;align-items:center;gap:10px;margin:0;padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--border, #e5e7eb);background:var(--surface, #fff)}.btn.ghost{border:1px solid var(--border);background:transparent;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.ghost.sm{padding:6px 12px;font-size:12px;border-radius:10px}.btn.primary{background:var(--primary, #5b6cff);color:#fff;border:none;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.primary:disabled{opacity:.65;cursor:not-allowed}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.mh-combo-field{position:relative;z-index:1}.medical-form .mh-combo-field:has(.mh-combo.open){z-index:50}.mh-combo{position:relative;z-index:2}.mh-combo.open{z-index:10}.mh-combo-trigger{width:100%;display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff);cursor:pointer;text-align:left}.mh-combo-trigger__icon{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.mh-combo-trigger__icon i{font-size:14px}.mh-combo-trigger__text{flex:1;min-width:0;font-weight:600;color:var(--text)}.mh-combo-chev{font-size:12px;color:var(--muted);transition:transform .15s}.mh-combo.open .mh-combo-chev{transform:rotate(-180deg)}.mh-combo-input{width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.mh-combo-panel{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:20;border-radius:12px;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);box-shadow:0 12px 28px -12px #0f172a40;overflow:hidden}.mh-combo-search{width:100%;border:none;border-bottom:1px solid var(--border, #e5e7eb);padding:10px 12px;font-size:14px;font-family:inherit;outline:none;background:var(--surface-2, #f8fafc)}.mh-combo-list{list-style:none;margin:0;padding:6px;max-height:220px;overflow-y:auto}.mh-combo-list li{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:8px;cursor:pointer;font-size:14px;line-height:1.3}.mh-combo-list li i{color:var(--muted);flex-shrink:0}.mh-combo-list li:hover{background:var(--surface-2, #f1f5f9)}.mh-combo-list li.active{background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);font-weight:600}.mh-combo-list--places li{font-weight:500}.mh-combo-add{display:flex;align-items:center;gap:8px;width:calc(100% - 12px);margin:6px;padding:10px 12px;border-radius:10px;border:1px dashed var(--border, #cbd5e1);background:color-mix(in srgb,var(--primary, #5b6cff) 8%,white);color:var(--primary, #5b6cff);font-size:13px;font-weight:700;cursor:pointer;font-family:inherit}.mh-combo-add:hover{filter:brightness(.97)}.med-attach-field{margin-top:0}.med-preview{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px}.med-preview img{max-width:100%;max-height:120px;border-radius:12px;border:1px solid var(--border, #e5e7eb);object-fit:contain}.med-upload-card{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;margin:0;border-radius:14px;border:1px solid var(--border, #e5e7eb);background:linear-gradient(135deg,color-mix(in srgb,var(--primary, #5b6cff) 6%,white),var(--surface, #fff));cursor:pointer;font-family:inherit;box-sizing:border-box;transition:border-color .15s,box-shadow .15s}.med-upload-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 45%,#e5e7eb);box-shadow:0 6px 20px -12px #5b6cff59}.med-upload-card:active{transform:scale(.99)}.med-upload-card:focus-within{outline:none;border-color:var(--primary, #5b6cff);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary, #5b6cff) 24%,transparent)}.med-upload-card__icon{flex-shrink:0;width:38px;height:38px;display:flex;align-items:center;justify-content:center;border-radius:12px;background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);color:var(--primary, #5b6cff);font-size:18px}.med-upload-card__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left}.med-upload-card__title{font-size:14px;font-weight:800;color:var(--text);line-height:1.25}.med-upload-card__arrow{flex-shrink:0;color:var(--muted);font-size:14px;opacity:.85}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: RichTextEditorComponent, selector: "app-rich-text-editor", inputs: ["placeholder", "minHeight"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11388
11563
  }
11389
11564
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: MedicalHistoryDialogComponent, decorators: [{
11390
11565
  type: Component,
11391
- args: [{ selector: 'app-medical-history-dialog', standalone: true, imports: [FormsModule, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (dialogOpen()) {\n <div class=\"dialog-overlay\" (click)=\"closeDialog()\">\n <div\n class=\"dialog medical-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head\">\n <div>\n <h2>\n <i class=\"pi pi-pencil\"></i>\n {{ editingEntry() ? 'S\u1EEDa s\u1EF1 ki\u1EC7n' : 'Th\u00EAm s\u1EF1 ki\u1EC7n' }}\n </h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n @if (errorMsg()) {\n <div class=\"alert error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n {{ errorMsg() }}\n </div>\n }\n <form class=\"medical-form\" (ngSubmit)=\"submitDialog()\">\n <label class=\"field\">\n <span class=\"label\">Ng\u00E0y</span>\n <input\n type=\"date\"\n [ngModel]=\"draft().date\"\n (ngModelChange)=\"updateDraft({ date: $event })\"\n name=\"mhDate\"\n [max]=\"maxDateToday\"\n required\n />\n </label>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">Lo\u1EA1i</span>\n <div class=\"mh-combo\" [class.open]=\"kindPickerOpen()\">\n <button\n type=\"button\"\n class=\"mh-combo-trigger\"\n (click)=\"toggleKindPicker(); $event.stopPropagation()\"\n >\n <span class=\"mh-combo-trigger__icon\"\n ><i class=\"fa-solid\" [ngClass]=\"metaFor(draft().kind).icon\"></i\n ></span>\n <span class=\"mh-combo-trigger__text\">{{ metaFor(draft().kind).label }}</span>\n <i class=\"pi pi-chevron-down mh-combo-chev\"></i>\n </button>\n @if (kindPickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"search\"\n class=\"mh-combo-search\"\n placeholder=\"T\u00ECm lo\u1EA1i\u2026\"\n [ngModel]=\"kindSearch()\"\n (ngModelChange)=\"kindSearch.set($event)\"\n name=\"mhKindSearch\"\n autocomplete=\"off\"\n (click)=\"$event.stopPropagation()\"\n />\n <ul class=\"mh-combo-list\" role=\"listbox\">\n @for (km of filteredKindCatalog(); track km) {\n <li\n role=\"option\"\n [class.active]=\"draft().kind === km.id\"\n (click)=\"selectKind(km.id)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"km.icon\"></i> {{ km.label }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <label class=\"field\">\n <span class=\"label\">Ti\u00EAu \u0111\u1EC1</span>\n <input\n type=\"text\"\n [ngModel]=\"draft().title\"\n (ngModelChange)=\"updateDraft({ title: $event })\"\n name=\"mhTitle\"\n placeholder=\"VD: M\u0169i 2 Pentaxim \u2014 BV Nhi\"\n required\n />\n </label>\n <label class=\"field\">\n <span class=\"label\">Chi ti\u1EBFt</span>\n <textarea\n [ngModel]=\"draft().detail\"\n (ngModelChange)=\"updateDraft({ detail: $event })\"\n name=\"mhDetail\"\n rows=\"3\"\n placeholder=\"Ph\u1EA3n \u1EE9ng, ch\u1EC9 \u0111\u1ECBnh BS, li\u1EC1u\u2026\"\n ></textarea>\n </label>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">N\u01A1i kh\u00E1m</span>\n <div class=\"mh-combo mh-place-combo\" [class.open]=\"placePickerOpen()\">\n <input\n type=\"text\"\n class=\"mh-combo-input\"\n [ngModel]=\"draft().place\"\n (ngModelChange)=\"\n updateDraft({ place: $event }); kindPickerOpen.set(false); placePickerOpen.set(true)\n \"\n (focus)=\"onPlaceFieldFocus()\"\n name=\"mhPlace\"\n placeholder=\"Ch\u1ECDn ho\u1EB7c g\u00F5 t\u00EAn c\u01A1 s\u1EDF\u2026\"\n autocomplete=\"off\"\n />\n @if (placePickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n @if (canAddCustomPlace()) {\n <button type=\"button\" class=\"mh-combo-add\" (click)=\"addCustomPlaceFromDraft()\">\n <i class=\"pi pi-plus\"></i> Th\u00EAm \u00AB{{ draft().place.trim() }}\u00BB v\u00E0o danh s\u00E1ch\n </button>\n }\n <ul class=\"mh-combo-list mh-combo-list--places\">\n @for (p of filteredPlaceSuggestions(); track p) {\n <li (click)=\"selectPlace(p)\">\n {{ p }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <div class=\"field med-attach-field\">\n <span class=\"label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</span>\n @if (\n editingEntry() &&\n !pendingAttachmentPreviewUrl() &&\n !stripExistingAttachment() &&\n attachmentFor(editingEntry()!);\n as sav\n ) {\n <div class=\"med-preview med-preview--saved\">\n @if (sav.content || sav.previewUrl) {\n <img [src]=\"sav.previewUrl || sav.content\" alt=\"\u0110\u00EDnh k\u00E8m hi\u1EC7n t\u1EA1i\" />\n }\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"removeSavedAttachment()\">\n <i class=\"pi pi-times\"></i> G\u1EE1 \u1EA3nh \u0111\u00E3 l\u01B0u\n </button>\n </div>\n }\n @if (pendingAttachmentPreviewUrl(); as prevUrl) {\n <div class=\"med-preview med-preview--new\">\n <img [src]=\"prevUrl\" alt=\"Xem tr\u01B0\u1EDBc\" />\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"clearPendingAttachment()\">\n Hu\u1EF7 \u1EA3nh m\u1EDBi\n </button>\n </div>\n }\n <label class=\"med-upload-card\">\n <input\n type=\"file\"\n accept=\"image/*,.heic,.heif\"\n class=\"sr-only\"\n (change)=\"onAttachmentInput($any($event.target))\"\n />\n <span class=\"med-upload-card__icon\" aria-hidden=\"true\">\n <i class=\"pi pi-images\"></i>\n </span>\n <span class=\"med-upload-card__text\">\n <strong class=\"med-upload-card__title\">{{\n pendingAttachment() ? '\u0110\u1ED5i \u1EA3nh' : 'Ch\u1ECDn \u1EA3nh'\n }}</strong>\n </span>\n <span class=\"med-upload-card__arrow\" aria-hidden=\"true\">\n <i class=\"pi pi-angle-right\"></i>\n </span>\n </label>\n </div>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeDialog()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"submit\" class=\"btn primary\" [disabled]=\"saving()\">\n @if (!saving()) {\n <i class=\"pi pi-check\"></i>\n }\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\"></i>\n }\n {{ editingEntry() ? 'C\u1EADp nh\u1EADt' : 'L\u01B0u' }}\n </button>\n </div>\n </form>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.medical-dialog{width:100%;max-width:480px;max-height:90vh;overflow:auto;background:var(--surface, #fff);border-radius:0;padding:0 0 12px;box-shadow:0 24px 48px -24px #0f172a59}@media(min-width:640px){.medical-dialog{max-height:min(88dvh,720px);padding-bottom:env(safe-area-inset-bottom,12px)}}.medical-dialog .field input,.medical-dialog .field select,.medical-dialog .field textarea,.medical-dialog .mh-combo-trigger,.medical-dialog .mh-combo-input{padding:8px 10px;border-radius:0}.medical-dialog .mh-combo-panel,.medical-dialog .mh-combo-search,.medical-dialog .med-upload-card{border-radius:0}.medical-dialog .alert.error{display:flex;align-items:flex-start;gap:10px;margin:0 16px 8px;padding:10px 12px;border-radius:12px;font-size:13px;background:#fef2f2;color:#b91c1c;border:1px solid #fecaca}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:14px 16px 10px;border-bottom:1px solid var(--border, #e5e7eb)}.dialog-head h2{margin:0;font-size:1.1rem;font-weight:800;display:flex;align-items:center;gap:8px}.dialog-close{border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px}.dialog-close:hover{background:var(--surface-2, #f1f5f9)}.medical-form{padding:12px 16px 16px;display:flex;flex-direction:column;gap:10px}.field{display:flex;flex-direction:column;gap:4px}.field .label{font-size:12px;font-weight:700;color:var(--muted)}.field input,.field select,.field textarea{padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.field textarea{resize:vertical;min-height:72px}.dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px;padding-top:4px}.btn.ghost{border:1px solid var(--border);background:transparent;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.ghost.sm{padding:6px 12px;font-size:12px;border-radius:10px}.btn.primary{background:var(--primary, #5b6cff);color:#fff;border:none;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.primary:disabled{opacity:.65;cursor:not-allowed}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.mh-combo-field{position:relative;z-index:1}.medical-form .mh-combo-field:has(.mh-combo.open){z-index:50}.mh-combo{position:relative;z-index:2}.mh-combo.open{z-index:10}.mh-combo-trigger{width:100%;display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff);cursor:pointer;text-align:left}.mh-combo-trigger__icon{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.mh-combo-trigger__icon i{font-size:14px}.mh-combo-trigger__text{flex:1;min-width:0;font-weight:600;color:var(--text)}.mh-combo-chev{font-size:12px;color:var(--muted);transition:transform .15s}.mh-combo.open .mh-combo-chev{transform:rotate(-180deg)}.mh-combo-input{width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.mh-combo-panel{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:20;border-radius:12px;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);box-shadow:0 12px 28px -12px #0f172a40;overflow:hidden}.mh-combo-search{width:100%;border:none;border-bottom:1px solid var(--border, #e5e7eb);padding:10px 12px;font-size:14px;font-family:inherit;outline:none;background:var(--surface-2, #f8fafc)}.mh-combo-list{list-style:none;margin:0;padding:6px;max-height:220px;overflow-y:auto}.mh-combo-list li{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:8px;cursor:pointer;font-size:14px;line-height:1.3}.mh-combo-list li i{color:var(--muted);flex-shrink:0}.mh-combo-list li:hover{background:var(--surface-2, #f1f5f9)}.mh-combo-list li.active{background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);font-weight:600}.mh-combo-list--places li{font-weight:500}.mh-combo-add{display:flex;align-items:center;gap:8px;width:calc(100% - 12px);margin:6px;padding:10px 12px;border-radius:10px;border:1px dashed var(--border, #cbd5e1);background:color-mix(in srgb,var(--primary, #5b6cff) 8%,white);color:var(--primary, #5b6cff);font-size:13px;font-weight:700;cursor:pointer;font-family:inherit}.mh-combo-add:hover{filter:brightness(.97)}.med-attach-field{margin-top:0}.med-preview{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px}.med-preview img{max-width:100%;max-height:120px;border-radius:12px;border:1px solid var(--border, #e5e7eb);object-fit:contain}.med-upload-card{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;margin:0;border-radius:14px;border:1px solid var(--border, #e5e7eb);background:linear-gradient(135deg,color-mix(in srgb,var(--primary, #5b6cff) 6%,white),var(--surface, #fff));cursor:pointer;font-family:inherit;box-sizing:border-box;transition:border-color .15s,box-shadow .15s}.med-upload-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 45%,#e5e7eb);box-shadow:0 6px 20px -12px #5b6cff59}.med-upload-card:active{transform:scale(.99)}.med-upload-card:focus-within{outline:none;border-color:var(--primary, #5b6cff);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary, #5b6cff) 24%,transparent)}.med-upload-card__icon{flex-shrink:0;width:38px;height:38px;display:flex;align-items:center;justify-content:center;border-radius:12px;background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);color:var(--primary, #5b6cff);font-size:18px}.med-upload-card__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left}.med-upload-card__title{font-size:14px;font-weight:800;color:var(--text);line-height:1.25}.med-upload-card__arrow{flex-shrink:0;color:var(--muted);font-size:14px;opacity:.85}\n"] }]
11566
+ args: [{ selector: 'app-medical-history-dialog', standalone: true, imports: [FormsModule, NgClass, RichTextEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (dialogOpen()) {\n <div class=\"dialog-overlay\" (click)=\"closeDialog()\">\n <div\n class=\"dialog medical-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head\">\n <div>\n <h2>\n <i class=\"pi pi-pencil\"></i>\n {{ editingEntry() ? 'S\u1EEDa s\u1EF1 ki\u1EC7n' : 'Th\u00EAm s\u1EF1 ki\u1EC7n' }}\n </h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"closeDialog()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n @if (errorMsg()) {\n <div class=\"alert error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n {{ errorMsg() }}\n </div>\n }\n <form class=\"medical-form\" (ngSubmit)=\"submitDialog()\">\n <div class=\"medical-form__body\">\n <label class=\"field\">\n <span class=\"label\">Ng\u00E0y</span>\n <input\n type=\"date\"\n [ngModel]=\"draft().date\"\n (ngModelChange)=\"updateDraft({ date: $event })\"\n name=\"mhDate\"\n [max]=\"maxDateToday\"\n required\n />\n </label>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">Lo\u1EA1i</span>\n <div class=\"mh-combo\" [class.open]=\"kindPickerOpen()\">\n <button\n type=\"button\"\n class=\"mh-combo-trigger\"\n (click)=\"toggleKindPicker(); $event.stopPropagation()\"\n >\n <span class=\"mh-combo-trigger__icon\"\n ><i class=\"fa-solid\" [ngClass]=\"metaFor(draft().kind).icon\"></i\n ></span>\n <span class=\"mh-combo-trigger__text\">{{ metaFor(draft().kind).label }}</span>\n <i class=\"pi pi-chevron-down mh-combo-chev\"></i>\n </button>\n @if (kindPickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"search\"\n class=\"mh-combo-search\"\n placeholder=\"T\u00ECm lo\u1EA1i\u2026\"\n [ngModel]=\"kindSearch()\"\n (ngModelChange)=\"kindSearch.set($event)\"\n name=\"mhKindSearch\"\n autocomplete=\"off\"\n (click)=\"$event.stopPropagation()\"\n />\n <ul class=\"mh-combo-list\" role=\"listbox\">\n @for (km of filteredKindCatalog(); track km) {\n <li\n role=\"option\"\n [class.active]=\"draft().kind === km.id\"\n (click)=\"selectKind(km.id)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"km.icon\"></i> {{ km.label }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <label class=\"field\">\n <span class=\"label\">Ti\u00EAu \u0111\u1EC1</span>\n <input\n type=\"text\"\n [ngModel]=\"draft().title\"\n (ngModelChange)=\"updateDraft({ title: $event })\"\n name=\"mhTitle\"\n placeholder=\"VD: M\u0169i 2 Pentaxim \u2014 BV Nhi\"\n required\n />\n </label>\n <div class=\"field field--rich\">\n <span class=\"label\">Chi ti\u1EBFt</span>\n <app-rich-text-editor\n [ngModel]=\"draft().detail\"\n (ngModelChange)=\"updateDraft({ detail: $event })\"\n name=\"mhDetail\"\n [minHeight]=\"200\"\n placeholder=\"Ph\u1EA3n \u1EE9ng, ch\u1EC9 \u0111\u1ECBnh BS, li\u1EC1u\u2026\"\n />\n </div>\n <div class=\"field mh-combo-field\">\n <span class=\"label\">N\u01A1i kh\u00E1m</span>\n <div class=\"mh-combo mh-place-combo\" [class.open]=\"placePickerOpen()\">\n <input\n type=\"text\"\n class=\"mh-combo-input\"\n [ngModel]=\"draft().place\"\n (ngModelChange)=\"\n updateDraft({ place: $event });\n kindPickerOpen.set(false);\n placePickerOpen.set(true)\n \"\n (focus)=\"onPlaceFieldFocus()\"\n name=\"mhPlace\"\n placeholder=\"Ch\u1ECDn ho\u1EB7c g\u00F5 t\u00EAn c\u01A1 s\u1EDF\u2026\"\n autocomplete=\"off\"\n />\n @if (placePickerOpen()) {\n <div class=\"mh-combo-panel\" (click)=\"$event.stopPropagation()\">\n @if (canAddCustomPlace()) {\n <button type=\"button\" class=\"mh-combo-add\" (click)=\"addCustomPlaceFromDraft()\">\n <i class=\"pi pi-plus\"></i> Th\u00EAm \u00AB{{ draft().place.trim() }}\u00BB v\u00E0o danh s\u00E1ch\n </button>\n }\n <ul class=\"mh-combo-list mh-combo-list--places\">\n @for (p of filteredPlaceSuggestions(); track p) {\n <li (click)=\"selectPlace(p)\">\n {{ p }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n </div>\n <div class=\"field med-attach-field\">\n <span class=\"label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</span>\n @if (\n editingEntry() &&\n !pendingAttachmentPreviewUrl() &&\n !stripExistingAttachment() &&\n attachmentFor(editingEntry()!);\n as sav\n ) {\n <div class=\"med-preview med-preview--saved\">\n @if (sav.content || sav.previewUrl) {\n <img [src]=\"sav.previewUrl || sav.content\" alt=\"\u0110\u00EDnh k\u00E8m hi\u1EC7n t\u1EA1i\" />\n }\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"removeSavedAttachment()\">\n <i class=\"pi pi-times\"></i> G\u1EE1 \u1EA3nh \u0111\u00E3 l\u01B0u\n </button>\n </div>\n }\n @if (pendingAttachmentPreviewUrl(); as prevUrl) {\n <div class=\"med-preview med-preview--new\">\n <img [src]=\"prevUrl\" alt=\"Xem tr\u01B0\u1EDBc\" />\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"clearPendingAttachment()\">\n Hu\u1EF7 \u1EA3nh m\u1EDBi\n </button>\n </div>\n }\n <label class=\"med-upload-card\">\n <input\n type=\"file\"\n accept=\"image/*,.heic,.heif\"\n class=\"sr-only\"\n (change)=\"onAttachmentInput($any($event.target))\"\n />\n <span class=\"med-upload-card__icon\" aria-hidden=\"true\">\n <i class=\"pi pi-images\"></i>\n </span>\n <span class=\"med-upload-card__text\">\n <strong class=\"med-upload-card__title\">{{\n pendingAttachment() ? '\u0110\u1ED5i \u1EA3nh' : 'Ch\u1ECDn \u1EA3nh'\n }}</strong>\n </span>\n <span class=\"med-upload-card__arrow\" aria-hidden=\"true\">\n <i class=\"pi pi-angle-right\"></i>\n </span>\n </label>\n </div>\n </div>\n <div class=\"dialog-actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"closeDialog()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"submit\" class=\"btn primary\" [disabled]=\"saving()\">\n @if (!saving()) {\n <i class=\"pi pi-check\"></i>\n }\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\"></i>\n }\n {{ editingEntry() ? 'C\u1EADp nh\u1EADt' : 'L\u01B0u' }}\n </button>\n </div>\n </form>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.medical-dialog{width:100%;max-width:480px;max-height:min(90vh,720px);overflow:hidden;display:flex;flex-direction:column;background:var(--surface, #fff);border-radius:0;padding:0;box-shadow:0 24px 48px -24px #0f172a59}@media(min-width:640px){.medical-dialog{max-height:min(88dvh,720px)}}.medical-dialog .field input,.medical-dialog .field select,.medical-dialog .field textarea,.medical-dialog .mh-combo-trigger,.medical-dialog .mh-combo-input{padding:8px 10px;border-radius:0}.medical-dialog .mh-combo-panel,.medical-dialog .mh-combo-search,.medical-dialog .med-upload-card{border-radius:0}.medical-dialog .alert.error{display:flex;align-items:flex-start;gap:10px;flex-shrink:0;margin:0 16px 8px;padding:10px 12px;border-radius:12px;font-size:13px;background:#fef2f2;color:#b91c1c;border:1px solid #fecaca}.dialog-head{flex-shrink:0;display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:14px 16px 10px;border-bottom:1px solid var(--border, #e5e7eb)}.dialog-head h2{margin:0;font-size:1.1rem;font-weight:800;display:flex;align-items:center;gap:8px}.dialog-close{border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px}.dialog-close:hover{background:var(--surface-2, #f1f5f9)}.medical-form{flex:1;min-height:0;display:flex;flex-direction:column;padding:0;gap:0}.medical-form__body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:12px 16px;display:flex;flex-direction:column;gap:10px;-webkit-overflow-scrolling:touch}.field{display:flex;flex-direction:column;gap:4px}.field .label{font-size:12px;font-weight:700;color:var(--muted)}.field input,.field select,.field textarea{padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.field textarea{resize:vertical;min-height:72px}.field--rich .label{margin-bottom:2px}.dialog-actions{flex-shrink:0;display:flex;justify-content:flex-end;align-items:center;gap:10px;margin:0;padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--border, #e5e7eb);background:var(--surface, #fff)}.btn.ghost{border:1px solid var(--border);background:transparent;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.ghost.sm{padding:6px 12px;font-size:12px;border-radius:10px}.btn.primary{background:var(--primary, #5b6cff);color:#fff;border:none;padding:10px 18px;border-radius:12px;font-weight:700;cursor:pointer;font-family:inherit}.btn.primary:disabled{opacity:.65;cursor:not-allowed}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.mh-combo-field{position:relative;z-index:1}.medical-form .mh-combo-field:has(.mh-combo.open){z-index:50}.mh-combo{position:relative;z-index:2}.mh-combo.open{z-index:10}.mh-combo-trigger{width:100%;display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff);cursor:pointer;text-align:left}.mh-combo-trigger__icon{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:8px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.mh-combo-trigger__icon i{font-size:14px}.mh-combo-trigger__text{flex:1;min-width:0;font-weight:600;color:var(--text)}.mh-combo-chev{font-size:12px;color:var(--muted);transition:transform .15s}.mh-combo.open .mh-combo-chev{transform:rotate(-180deg)}.mh-combo-input{width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.mh-combo-panel{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:20;border-radius:12px;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);box-shadow:0 12px 28px -12px #0f172a40;overflow:hidden}.mh-combo-search{width:100%;border:none;border-bottom:1px solid var(--border, #e5e7eb);padding:10px 12px;font-size:14px;font-family:inherit;outline:none;background:var(--surface-2, #f8fafc)}.mh-combo-list{list-style:none;margin:0;padding:6px;max-height:220px;overflow-y:auto}.mh-combo-list li{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:8px;cursor:pointer;font-size:14px;line-height:1.3}.mh-combo-list li i{color:var(--muted);flex-shrink:0}.mh-combo-list li:hover{background:var(--surface-2, #f1f5f9)}.mh-combo-list li.active{background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);font-weight:600}.mh-combo-list--places li{font-weight:500}.mh-combo-add{display:flex;align-items:center;gap:8px;width:calc(100% - 12px);margin:6px;padding:10px 12px;border-radius:10px;border:1px dashed var(--border, #cbd5e1);background:color-mix(in srgb,var(--primary, #5b6cff) 8%,white);color:var(--primary, #5b6cff);font-size:13px;font-weight:700;cursor:pointer;font-family:inherit}.mh-combo-add:hover{filter:brightness(.97)}.med-attach-field{margin-top:0}.med-preview{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px}.med-preview img{max-width:100%;max-height:120px;border-radius:12px;border:1px solid var(--border, #e5e7eb);object-fit:contain}.med-upload-card{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;margin:0;border-radius:14px;border:1px solid var(--border, #e5e7eb);background:linear-gradient(135deg,color-mix(in srgb,var(--primary, #5b6cff) 6%,white),var(--surface, #fff));cursor:pointer;font-family:inherit;box-sizing:border-box;transition:border-color .15s,box-shadow .15s}.med-upload-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 45%,#e5e7eb);box-shadow:0 6px 20px -12px #5b6cff59}.med-upload-card:active{transform:scale(.99)}.med-upload-card:focus-within{outline:none;border-color:var(--primary, #5b6cff);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary, #5b6cff) 24%,transparent)}.med-upload-card__icon{flex-shrink:0;width:38px;height:38px;display:flex;align-items:center;justify-content:center;border-radius:12px;background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);color:var(--primary, #5b6cff);font-size:18px}.med-upload-card__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left}.med-upload-card__title{font-size:14px;font-weight:800;color:var(--text);line-height:1.25}.med-upload-card__arrow{flex-shrink:0;color:var(--muted);font-size:14px;opacity:.85}\n"] }]
11392
11567
  }], ctorParameters: () => [], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], explorerEntries: [{ type: i0.Input, args: [{ isSignal: true, alias: "explorerEntries", required: false }] }], medicalFolderId: [{ type: i0.Input, args: [{ isSignal: true, alias: "medicalFolderId", required: false }] }], entries: [{ type: i0.Input, args: [{ isSignal: true, alias: "entries", required: false }] }], closed: [{
11393
11568
  type: Output
11394
11569
  }], saved: [{
@@ -11446,6 +11621,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
11446
11621
  args: [{ selector: 'app-medical-history-filter', standalone: true, imports: [FormsModule, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!loading() && myEntriesCount() > 0) {\n <div class=\"mh-toolbar card\">\n <div class=\"mh-toolbar__search-row\">\n <label class=\"mh-toolbar__search-label\" for=\"mhFreeTextSearch\">\n <i class=\"pi pi-search\" aria-hidden=\"true\"></i>\n T\u00ECm trong ti\u1EC1n s\u1EED\n </label>\n <div class=\"mh-toolbar__search\">\n <input\n id=\"mhFreeTextSearch\"\n type=\"search\"\n class=\"mh-toolbar__search-input\"\n placeholder=\"Ti\u00EAu \u0111\u1EC1, chi ti\u1EBFt, n\u01A1i kh\u00E1m\u2026\"\n [ngModel]=\"textSearchQuery()\"\n (ngModelChange)=\"onTextSearchChange($event)\"\n name=\"mhFreeTextSearch\"\n autocomplete=\"off\"\n enterkeyhint=\"search\"\n />\n @if (textSearchQuery().trim()) {\n <button\n type=\"button\"\n class=\"mh-toolbar__search-clear btn ghost sm\"\n (click)=\"clearTextSearch.emit()\"\n aria-label=\"Xo\u00E1 \u00F4 t\u00ECm\"\n title=\"Xo\u00E1 \u00F4 t\u00ECm\"\n >\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </div>\n\n <div class=\"mh-toolbar__pills\" role=\"toolbar\" aria-label=\"L\u1ECDc theo lo\u1EA1i s\u1EF1 ki\u1EC7n\">\n <button\n type=\"button\"\n class=\"mh-kind-pill\"\n [class.active]=\"kindFilterKinds().length === 0\"\n (click)=\"clearKindFilters.emit()\"\n >\n T\u1EA5t c\u1EA3\n <span class=\"mh-kind-pill__count\">{{ myEntriesCount() }}</span>\n </button>\n @for (km of quickKinds(); track km.id) {\n <button\n type=\"button\"\n class=\"mh-kind-pill\"\n [class.active]=\"kindFilterKindSet().has(km.id)\"\n [style.--chip-color]=\"km.color\"\n (click)=\"toggleKindFilter.emit(km.id)\"\n [attr.title]=\"km.label\"\n >\n <i class=\"fa-solid\" [ngClass]=\"km.icon\" aria-hidden=\"true\"></i>\n {{ km.shortLabel }}\n <span class=\"mh-kind-pill__count\">{{ kindCounts()[km.id] }}</span>\n </button>\n }\n <button\n type=\"button\"\n class=\"mh-kind-pill mh-kind-pill--more\"\n [class.active]=\"filterPanelOpen()\"\n (click)=\"toggleFilterPanel.emit(); $event.stopPropagation()\"\n [attr.aria-expanded]=\"filterPanelOpen()\"\n aria-controls=\"medical-filter-panel\"\n >\n <i class=\"pi pi-sliders-h\" aria-hidden=\"true\"></i>\n Th\u00EAm\n </button>\n </div>\n\n <div class=\"mh-toolbar__meta\">\n <span class=\"mh-toolbar__count\">{{ visibleCount() }} hi\u1EC3n th\u1ECB</span>\n <button\n type=\"button\"\n class=\"mh-toolbar__sort\"\n (click)=\"onTimelineSort(timelineSort() === 'newest' ? 'oldest' : 'newest')\"\n [attr.title]=\"timelineSort() === 'newest' ? '\u0110ang x\u1EBFp m\u1EDBi nh\u1EA5t tr\u01B0\u1EDBc' : '\u0110ang x\u1EBFp c\u0169 nh\u1EA5t tr\u01B0\u1EDBc'\"\n >\n <i class=\"pi pi-sort-alt\" aria-hidden=\"true\"></i>\n {{ timelineSort() === 'newest' ? 'M\u1EDBi nh\u1EA5t' : 'C\u0169 nh\u1EA5t' }}\n </button>\n </div>\n </div>\n}\n\n<div class=\"mh-filter\" [class.mh-filter--open]=\"filterPanelOpen()\">\n @if (filterPanelOpen()) {\n <div\n class=\"mh-filter-panel card\"\n id=\"medical-filter-panel\"\n role=\"region\"\n aria-label=\"L\u1ECDc n\u00E2ng cao\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"mh-filter-panel__headrow\">\n <div class=\"mh-filter-panel__title\">L\u1ECDc &amp; s\u1EAFp x\u1EBFp</div>\n <button\n type=\"button\"\n class=\"mh-filter-panel__reset btn ghost sm\"\n (click)=\"resetFilterPanel.emit()\"\n title=\"\u0110\u1EB7t l\u1EA1i b\u1ED9 l\u1ECDc\"\n >\n <i class=\"pi pi-replay\" aria-hidden=\"true\"></i>\n \u0110\u1EB7t l\u1EA1i\n </button>\n </div>\n <div class=\"mh-filter-panel__field\">\n <span class=\"mh-filter-panel__label\">Th\u1EDDi gian hi\u1EC3n th\u1ECB</span>\n <div class=\"mh-filter-panel__seg\" role=\"group\" aria-label=\"S\u1EAFp x\u1EBFp timeline\">\n <button\n type=\"button\"\n [class.active]=\"timelineSort() === 'newest'\"\n (click)=\"onTimelineSort('newest')\"\n >\n M\u1EDBi nh\u1EA5t tr\u01B0\u1EDBc\n </button>\n <button\n type=\"button\"\n [class.active]=\"timelineSort() === 'oldest'\"\n (click)=\"onTimelineSort('oldest')\"\n >\n C\u0169 nh\u1EA5t tr\u01B0\u1EDBc\n </button>\n </div>\n </div>\n <div class=\"mh-filter-panel__field\">\n <label class=\"mh-filter-panel__label\" for=\"mhFilterKindSearch\">T\u00ECm lo\u1EA1i</label>\n <input\n id=\"mhFilterKindSearch\"\n type=\"search\"\n class=\"mh-filter-panel__search\"\n placeholder=\"VD: tiem, kham, o / \u00F4\u2026\"\n [ngModel]=\"filterPanelKindSearch()\"\n (ngModelChange)=\"onFilterPanelKindSearchChange($event)\"\n name=\"mhFilterKindSearch\"\n autocomplete=\"off\"\n />\n </div>\n <div class=\"mh-filter-panel__field\">\n <span class=\"mh-filter-panel__label\">S\u1EAFp danh s\u00E1ch lo\u1EA1i</span>\n <div class=\"mh-filter-panel__seg\" role=\"group\" aria-label=\"S\u1EAFp lo\u1EA1i\">\n <button\n type=\"button\"\n [class.active]=\"kindListSort() === 'count'\"\n (click)=\"onKindListSort('count')\"\n >\n Nhi\u1EC1u s\u1EF1 ki\u1EC7n tr\u01B0\u1EDBc\n </button>\n <button\n type=\"button\"\n [class.active]=\"kindListSort() === 'alpha'\"\n (click)=\"onKindListSort('alpha')\"\n >\n A \u2192 Z\n </button>\n </div>\n </div>\n <div\n class=\"mh-filter-panel__kinds\"\n role=\"listbox\"\n aria-label=\"Ch\u1ECDn lo\u1EA1i s\u1EF1 ki\u1EC7n\"\n aria-multiselectable=\"true\"\n >\n <button\n type=\"button\"\n class=\"kind-chip mh-filter-kind\"\n [class.active]=\"kindFilterKinds().length === 0\"\n (click)=\"clearKindFilters.emit()\"\n role=\"option\"\n [attr.aria-selected]=\"kindFilterKinds().length === 0\"\n >\n T\u1EA5t c\u1EA3\n <span class=\"kind-chip__count\">{{ myEntriesCount() }}</span>\n </button>\n @for (km of kindsInFilterPanel(); track km.id) {\n <button\n type=\"button\"\n class=\"kind-chip mh-filter-kind\"\n [class.active]=\"kindFilterKindSet().has(km.id)\"\n [style.--chip-color]=\"km.color\"\n (click)=\"toggleKindFilter.emit(km.id)\"\n [attr.title]=\"km.label\"\n role=\"option\"\n [attr.aria-selected]=\"kindFilterKindSet().has(km.id)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"km.icon\" aria-hidden=\"true\"></i>\n <span class=\"mh-filter-kind__name\">{{ km.shortLabel }}</span>\n <span class=\"kind-chip__count\">{{ kindCounts()[km.id] }}</span>\n </button>\n }\n @if (filterPanelKindSearch().trim() && kindsInFilterPanel().length === 0) {\n <p class=\"mh-filter-panel__empty\">\n Kh\u00F4ng th\u1EA5y lo\u1EA1i n\u00E0o \u2014 th\u1EED t\u1EEB ng\u1EAFn h\u01A1n (o/\u00F4, a/\u0103 t\u01B0\u01A1ng \u0111\u01B0\u01A1ng).\n </p>\n }\n </div>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.mh-toolbar{margin-bottom:12px;padding:14px 16px;display:flex;flex-direction:column;gap:12px}.mh-toolbar.card{border-radius:0;border:1px solid color-mix(in srgb,var(--border, #e5e7eb) 88%,var(--primary, #5b6cff));background:var(--surface, #fff)}.mh-toolbar__search-row{display:flex;flex-direction:column;gap:6px}.mh-toolbar__search-label{display:flex;align-items:center;gap:8px;font-size:11px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--muted, #64748b);margin:0}.mh-toolbar__search-label i{font-size:12px;color:var(--primary, #5b6cff)}.mh-toolbar__search{display:flex;align-items:stretch;gap:8px}.mh-toolbar__search-input{flex:1;min-width:0;padding:10px 12px;border-radius:0;border:1px solid var(--border, #e5e7eb);font-size:15px;font-family:inherit;background:var(--surface, #fff)}.mh-toolbar__search-input:focus{outline:none;border-color:var(--primary, #5b6cff)}.mh-toolbar__search-clear{flex-shrink:0;align-self:center;padding:8px 12px!important}.mh-toolbar__pills{display:flex;gap:6px;overflow-x:auto;padding-bottom:2px;-webkit-overflow-scrolling:touch;scrollbar-width:none}.mh-toolbar__pills::-webkit-scrollbar{display:none}.mh-toolbar__meta{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:2px;border-top:1px solid color-mix(in srgb,var(--border, #e5e7eb) 80%,transparent)}.mh-toolbar__count{font-size:12px;font-weight:700;color:var(--muted, #64748b);font-variant-numeric:tabular-nums}.mh-toolbar__sort{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border:none;background:transparent;font-family:inherit;font-size:12px;font-weight:700;color:var(--primary, #5b6cff);cursor:pointer}.mh-toolbar__sort:hover{text-decoration:underline}.mh-kind-pill{display:inline-flex;align-items:center;gap:5px;flex-shrink:0;padding:6px 11px;border-radius:999px;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);font-size:12px;font-weight:600;font-family:inherit;color:var(--text);cursor:pointer;transition:border-color .15s,background .15s}.mh-kind-pill i{font-size:12px;color:var(--chip-color, var(--muted))}.mh-kind-pill__count{font-size:10px;font-weight:800;opacity:.75;font-variant-numeric:tabular-nums}.mh-kind-pill.active{border-color:var(--chip-color, var(--primary, #5b6cff));background:color-mix(in srgb,var(--chip-color, var(--primary, #5b6cff)) 12%,white);font-weight:700}.mh-kind-pill--more{color:var(--muted, #64748b)}.mh-kind-pill--more.active{color:var(--primary, #5b6cff)}.mh-kind-pill:hover:not(.active){border-color:#cbd5e1}.mh-filter{position:relative;z-index:4;margin-bottom:16px}.mh-filter-panel{position:absolute;left:0;right:0;top:0;z-index:5;padding:14px 14px 12px;border-radius:0;border:1px solid color-mix(in srgb,var(--border, #e5e7eb) 80%,var(--primary, #5b6cff));background:var(--surface, #fff);box-shadow:0 20px 48px -24px #0f172a47}.mh-filter-panel__headrow{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}.mh-filter-panel__title{font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--muted, #64748b);margin:0;flex:1;min-width:0}.mh-filter-panel__reset{flex-shrink:0;white-space:nowrap;cursor:pointer;font-weight:700}.mh-filter-panel__reset i{margin-right:4px}.mh-filter-panel__field{margin-bottom:10px}.mh-filter-panel__label{display:block;font-size:11px;font-weight:700;color:var(--muted, #64748b);margin-bottom:6px}.mh-filter-panel__search{width:100%;padding:9px 12px;border-radius:0;border:1px solid var(--border, #e5e7eb);font-size:14px;font-family:inherit;background:var(--surface, #fff)}.mh-filter-panel__seg{display:flex;flex-wrap:wrap;gap:6px}.mh-filter-panel__seg button{flex:1;min-width:0;padding:8px 10px;border-radius:0;border:1px solid var(--border, #e5e7eb);background:var(--surface-2, #f8fafc);font-size:12px;font-weight:700;font-family:inherit;color:var(--muted, #64748b);cursor:pointer;transition:background .15s,border-color .15s,color .15s}.mh-filter-panel__seg button.active{border-color:var(--primary, #5b6cff);background:color-mix(in srgb,var(--primary, #5b6cff) 14%,white);color:var(--text)}.mh-filter-panel__seg button:hover:not(.active){border-color:#cbd5e1}.mh-filter-panel__kinds{margin-top:4px;max-height:min(52vh,280px);overflow-y:auto;display:flex;flex-direction:column;gap:6px;padding-right:2px}.mh-filter-panel__empty{margin:0;padding:8px 4px 4px;font-size:12px;line-height:1.4;color:var(--muted, #64748b)}.kind-chip{display:inline-flex;align-items:center;gap:6px;padding:8px 12px;border-radius:999px;border:1px solid var(--border, #e5e7eb);background:var(--surface, #fff);font-size:12px;font-weight:600;color:var(--text);cursor:pointer;transition:border-color .15s,background .15s,color .15s;font-family:inherit}.kind-chip i{font-size:13px;color:var(--chip-color, var(--muted))}.kind-chip__count{margin-left:auto;font-size:11px;font-weight:700;opacity:.75;font-variant-numeric:tabular-nums}.kind-chip.active{border-color:var(--chip-color, #5b6cff);background:color-mix(in srgb,var(--chip-color, #5b6cff) 12%,white);color:var(--text)}.kind-chip:hover:not(.active){border-color:#cbd5e1}.kind-chip.mh-filter-kind{width:100%;justify-content:flex-start;border-radius:0}.kind-chip.mh-filter-kind .mh-filter-kind__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.btn.ghost{border:1px solid var(--border);background:transparent}.btn.ghost.sm{padding:6px 12px;font-size:12px;border-radius:0}\n"] }]
11447
11622
  }], propDecorators: { loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], myEntriesCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "myEntriesCount", required: false }] }], visibleCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibleCount", required: false }] }], textSearchQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "textSearchQuery", required: false }] }], filterPanelOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPanelOpen", required: false }] }], filterPanelKindSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPanelKindSearch", required: false }] }], timelineSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineSort", required: false }] }], kindListSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "kindListSort", required: false }] }], kindFilterKinds: [{ type: i0.Input, args: [{ isSignal: true, alias: "kindFilterKinds", required: false }] }], kindFilterSummary: [{ type: i0.Input, args: [{ isSignal: true, alias: "kindFilterSummary", required: false }] }], kindsInFilterPanel: [{ type: i0.Input, args: [{ isSignal: true, alias: "kindsInFilterPanel", required: false }] }], kindCounts: [{ type: i0.Input, args: [{ isSignal: true, alias: "kindCounts", required: false }] }], kindFilterKindSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "kindFilterKindSet", required: false }] }], quickKinds: [{ type: i0.Input, args: [{ isSignal: true, alias: "quickKinds", required: false }] }], textSearchQueryChange: [{ type: i0.Output, args: ["textSearchQueryChange"] }], filterPanelOpenChange: [{ type: i0.Output, args: ["filterPanelOpenChange"] }], toggleFilterPanel: [{ type: i0.Output, args: ["toggleFilterPanel"] }], resetFilterPanel: [{ type: i0.Output, args: ["resetFilterPanel"] }], clearTextSearch: [{ type: i0.Output, args: ["clearTextSearch"] }], clearKindFilters: [{ type: i0.Output, args: ["clearKindFilters"] }], toggleKindFilter: [{ type: i0.Output, args: ["toggleKindFilter"] }], timelineSortChange: [{ type: i0.Output, args: ["timelineSortChange"] }], kindListSortChange: [{ type: i0.Output, args: ["kindListSortChange"] }], filterPanelKindSearchChange: [{ type: i0.Output, args: ["filterPanelKindSearchChange"] }] } });
11448
11623
 
11624
+ class SafeRichHtmlPipe {
11625
+ constructor() {
11626
+ this.sanitizer = inject(DomSanitizer);
11627
+ }
11628
+ transform(value) {
11629
+ return this.sanitizer.bypassSecurityTrustHtml(sanitizeRichHtml(value));
11630
+ }
11631
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SafeRichHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
11632
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: SafeRichHtmlPipe, isStandalone: true, name: "safeRichHtml" }); }
11633
+ }
11634
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SafeRichHtmlPipe, decorators: [{
11635
+ type: Pipe,
11636
+ args: [{
11637
+ name: 'safeRichHtml',
11638
+ standalone: true,
11639
+ pure: true,
11640
+ }]
11641
+ }] });
11642
+
11449
11643
  class MedicalHistoryTimelineComponent {
11450
11644
  constructor() {
11451
11645
  this.timelineGroups = input([], ...(ngDevMode ? [{ debugName: "timelineGroups" }] : /* istanbul ignore next */ []));
@@ -11481,11 +11675,11 @@ class MedicalHistoryTimelineComponent {
11481
11675
  img.src = this.placeholderSvg;
11482
11676
  }
11483
11677
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: MedicalHistoryTimelineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11484
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: MedicalHistoryTimelineComponent, isStandalone: true, selector: "app-medical-history-timeline", inputs: { timelineGroups: { classPropertyName: "timelineGroups", publicName: "timelineGroups", isSignal: true, isRequired: false, transformFunction: null }, resolveAttachment: { classPropertyName: "resolveAttachment", publicName: "resolveAttachment", isSignal: true, isRequired: false, transformFunction: null }, explorerEnabled: { classPropertyName: "explorerEnabled", publicName: "explorerEnabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editEntry: "editEntry", deleteEntry: "deleteEntry", viewEntry: "viewEntry", openPreview: "openPreview", openInDocuments: "openInDocuments" }, ngImport: i0, template: "<div class=\"mh-timeline\">\n @for (g of timelineGroups(); track g) {\n <section class=\"mh-month\">\n <header class=\"mh-month__header\">\n <span class=\"mh-month__dot\" aria-hidden=\"true\"></span>\n <h3 class=\"mh-month__title\">{{ g.monthLabel }}</h3>\n <span class=\"mh-month__line\" aria-hidden=\"true\"></span>\n <span class=\"mh-month__count\">{{ g.entries.length }}</span>\n </header>\n <ul class=\"mh-month__entries\">\n @for (e of g.entries; track e) {\n <li\n class=\"mh-card\"\n role=\"button\"\n (click)=\"onViewEntry(e)\"\n tabindex=\"0\"\n (keydown.enter)=\"onViewEntry(e)\"\n >\n <div class=\"mh-card__body\">\n <app-date-chip class=\"mh-card__date\" [date]=\"e.date\" />\n <div class=\"mh-card__main\">\n <div class=\"mh-card__top\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(e.kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(e.kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(e.kind).shortLabel }}\n </span>\n </div>\n <h4 class=\"mh-card__title\">{{ e.title }}</h4>\n @if (e.detail) {\n <p class=\"mh-card__detail\">{{ e.detail }}</p>\n }\n @if (e.place) {\n <p class=\"mh-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>{{ e.place }}\n </p>\n }\n <div class=\"mh-card__actions\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"btn ghost sm\"\n (click)=\"$event.stopPropagation(); editEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n @if (attachmentFor(e) && explorerEnabled()) {\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"$event.stopPropagation(); onOpenPreview(e)\">\n <i class=\"pi pi-image\"></i> \u1EA2nh\n </button>\n }\n <button\n type=\"button\"\n class=\"btn ghost sm danger\"\n (click)=\"$event.stopPropagation(); deleteEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-trash\"></i>\n </button>\n </div>\n </div>\n @if (explorerEnabled()) {\n @if (attachmentFor(e); as att) {\n <div class=\"mh-card__thumb\" (click)=\"$event.stopPropagation()\">\n @if (att.content || att.previewUrl || att.driveFileId) {\n <button\n type=\"button\"\n class=\"mh-card__thumb-btn\"\n (click)=\"onOpenPreview(e)\"\n aria-label=\"Xem \u1EA3nh \u0111\u00EDnh k\u00E8m\"\n >\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <img\n class=\"mh-card__thumb-img\"\n [src]=\"att.previewUrl || att.content || placeholderSvg\"\n alt=\"\"\n (error)=\"onImageError($event, att)\"\n />\n }\n </button>\n }\n </div>\n }\n }\n </div>\n <span\n class=\"mh-card__accent\"\n [style.--k]=\"metaFor(e.kind).color\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n }\n</div>\n", styles: [".mh-timeline{display:flex;flex-direction:column;gap:24px;margin-top:4px;padding-bottom:8px}.mh-month__header{display:flex;align-items:center;gap:10px;margin-bottom:10px;padding:0 2px}.mh-month__dot{width:8px;height:8px;flex-shrink:0;border-radius:50%;background:var(--primary, #5b6cff)}.mh-month__title{margin:0;font-size:14px;font-weight:800;letter-spacing:-.02em;color:var(--text);white-space:nowrap}.mh-month__line{flex:1;height:1px;background:color-mix(in srgb,var(--border, #e5e7eb) 85%,transparent)}.mh-month__count{flex-shrink:0;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--muted, #64748b)}.mh-month__entries{list-style:none;margin:0;padding:0 0 0 14px;border-left:2px solid color-mix(in srgb,var(--border, #e5e7eb) 90%,transparent);display:flex;flex-direction:column;gap:10px}.mh-card{position:relative;display:block;width:100%;box-sizing:border-box;border-radius:0;overflow:hidden;border:1px solid color-mix(in srgb,var(--border, #e5e7eb) 88%,var(--primary, #5b6cff));background:var(--surface, #fff);cursor:pointer;transition:border-color .2s ease}.mh-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 35%,var(--border, #e5e7eb))}.mh-card:focus-visible{outline:2px solid color-mix(in srgb,var(--primary, #5b6cff) 55%,white);outline-offset:2px}.mh-card__accent{position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--k, #64748b)}.mh-card__body{display:flex;align-items:flex-start;gap:12px;padding:12px 12px 12px 14px;min-width:0}.mh-card__main{flex:1;min-width:0}.mh-card__top{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:6px 10px;margin-bottom:8px}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;background:color-mix(in srgb,var(--k, #64748b) 14%,white);color:var(--text)}.mh-kind-badge i{font-size:12px;color:var(--k, #64748b)}.mh-card__date{flex-shrink:0;margin-top:1px}.mh-card__title{margin:0 0 6px;font-size:15px;font-weight:800;line-height:1.3;letter-spacing:-.02em}.mh-card__detail{margin:0 0 6px;font-size:13px;line-height:1.45;color:var(--text);white-space:pre-line}.mh-card__place{margin:0 0 8px;font-size:12px;color:var(--muted);display:flex;align-items:flex-start;gap:6px;line-height:1.4}.mh-card__place i{margin-top:2px;opacity:.85;flex-shrink:0}.mh-card__thumb{flex-shrink:0;width:56px;align-self:flex-start}.mh-card__thumb-btn,.mh-card__thumb-lock{display:flex;align-items:center;justify-content:center;width:56px;height:56px;padding:0;border:1px solid var(--border, #e5e7eb);background:var(--surface-2, #f1f5f9);cursor:pointer;overflow:hidden}.mh-card__thumb-btn{font:inherit;color:var(--muted)}.mh-card__thumb-btn:focus-visible{outline:2px solid var(--primary, #5b6cff);outline-offset:2px}.mh-card__thumb-lock{font-size:14px;color:var(--muted-2, #94a3b8)}.mh-card__thumb-img{display:block;width:100%;height:100%;object-fit:cover}.mh-card__actions{display:flex;gap:6px;flex-wrap:wrap;padding-top:6px;border-top:1px solid color-mix(in srgb,var(--border, #e5e7eb) 85%,transparent);margin-top:2px}.btn.ghost{border:1px solid var(--border);background:transparent}.btn.ghost.sm{padding:5px 10px;font-size:11px;border-radius:0}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}\n"], dependencies: [{ kind: "component", type: DateChipComponent, selector: "app-date-chip", inputs: ["date", "size", "interactive"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11678
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: MedicalHistoryTimelineComponent, isStandalone: true, selector: "app-medical-history-timeline", inputs: { timelineGroups: { classPropertyName: "timelineGroups", publicName: "timelineGroups", isSignal: true, isRequired: false, transformFunction: null }, resolveAttachment: { classPropertyName: "resolveAttachment", publicName: "resolveAttachment", isSignal: true, isRequired: false, transformFunction: null }, explorerEnabled: { classPropertyName: "explorerEnabled", publicName: "explorerEnabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editEntry: "editEntry", deleteEntry: "deleteEntry", viewEntry: "viewEntry", openPreview: "openPreview", openInDocuments: "openInDocuments" }, ngImport: i0, template: "<div class=\"mh-timeline\">\n @for (g of timelineGroups(); track g) {\n <section class=\"mh-month\">\n <header class=\"mh-month__header\">\n <span class=\"mh-month__dot\" aria-hidden=\"true\"></span>\n <h3 class=\"mh-month__title\">{{ g.monthLabel }}</h3>\n <span class=\"mh-month__line\" aria-hidden=\"true\"></span>\n <span class=\"mh-month__count\">{{ g.entries.length }}</span>\n </header>\n <ul class=\"mh-month__entries\">\n @for (e of g.entries; track e) {\n <li\n class=\"mh-card\"\n role=\"button\"\n (click)=\"onViewEntry(e)\"\n tabindex=\"0\"\n (keydown.enter)=\"onViewEntry(e)\"\n >\n <div class=\"mh-card__body\">\n <div class=\"mh-card__row1\">\n <app-date-chip class=\"mh-card__date\" [date]=\"e.date\" />\n <div class=\"mh-card__main\">\n <div class=\"mh-card__top\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(e.kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(e.kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(e.kind).shortLabel }}\n </span>\n </div>\n <h4 class=\"mh-card__title\">{{ e.title }}</h4>\n @if (e.place) {\n <p class=\"mh-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>{{ e.place }}\n </p>\n }\n </div>\n @if (explorerEnabled()) {\n @if (attachmentFor(e); as att) {\n <div class=\"mh-card__thumb\" (click)=\"$event.stopPropagation()\">\n @if (att.content || att.previewUrl || att.driveFileId) {\n <button\n type=\"button\"\n class=\"mh-card__thumb-btn\"\n (click)=\"onOpenPreview(e)\"\n aria-label=\"Xem \u1EA3nh \u0111\u00EDnh k\u00E8m\"\n >\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <img\n class=\"mh-card__thumb-img\"\n [src]=\"att.previewUrl || att.content || placeholderSvg\"\n alt=\"\"\n (error)=\"onImageError($event, att)\"\n />\n }\n </button>\n }\n </div>\n }\n }\n </div>\n\n @if (e.detail) {\n <div class=\"mh-card__detail\" [innerHTML]=\"e.detail | safeRichHtml\"></div>\n }\n\n <div class=\"mh-card__actions\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"btn ghost sm danger\"\n (click)=\"$event.stopPropagation(); deleteEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-trash\"></i> Xo\u00E1\n </button>\n <button\n type=\"button\"\n class=\"btn ghost sm\"\n (click)=\"$event.stopPropagation(); editEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n </div>\n </div>\n <span\n class=\"mh-card__accent\"\n [style.--k]=\"metaFor(e.kind).color\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n }\n</div>\n", styles: [".mh-timeline{display:flex;flex-direction:column;gap:24px;margin-top:4px;padding-bottom:8px}.mh-month__header{display:flex;align-items:center;gap:10px;margin-bottom:10px;padding:0 2px}.mh-month__dot{width:8px;height:8px;flex-shrink:0;border-radius:50%;background:var(--primary, #5b6cff)}.mh-month__title{margin:0;font-size:14px;font-weight:800;letter-spacing:-.02em;color:var(--text);white-space:nowrap}.mh-month__line{flex:1;height:1px;background:color-mix(in srgb,var(--border, #e5e7eb) 85%,transparent)}.mh-month__count{flex-shrink:0;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--muted, #64748b)}.mh-month__entries{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}.mh-card{position:relative;display:block;width:100%;box-sizing:border-box;border-radius:0;overflow:hidden;border:1px solid color-mix(in srgb,var(--border, #e5e7eb) 88%,var(--primary, #5b6cff));background:var(--surface, #fff);cursor:pointer;transition:border-color .2s ease}.mh-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 35%,var(--border, #e5e7eb))}.mh-card:focus-visible{outline:2px solid color-mix(in srgb,var(--primary, #5b6cff) 55%,white);outline-offset:2px}.mh-card__accent{position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--k, #64748b)}.mh-card__body{display:flex;flex-direction:column;gap:10px;padding:12px 12px 10px 14px;min-width:0}.mh-card__row1{display:flex;align-items:flex-start;gap:10px;min-width:0}.mh-card__date{flex-shrink:0}.mh-card__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.mh-card__top{display:flex;align-items:center}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;background:color-mix(in srgb,var(--k, #64748b) 14%,white);color:var(--text);white-space:nowrap}.mh-kind-badge i{font-size:12px;color:var(--k, #64748b)}.mh-card__title{margin:0;font-size:15px;font-weight:800;line-height:1.3;letter-spacing:-.02em;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.mh-card__place{margin:0;font-size:12px;color:var(--muted);display:flex;align-items:flex-start;gap:6px;line-height:1.4}.mh-card__place i{margin-top:2px;opacity:.85;flex-shrink:0}.mh-card__detail{margin:0;font-size:13px;line-height:1.45;color:var(--text);word-break:break-word;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}.mh-card__detail ::ng-deep p{margin:0;display:inline}.mh-card__detail ::ng-deep p:after{content:\" \"}.mh-card__detail ::ng-deep ul,.mh-card__detail ::ng-deep ol{margin:0;padding-left:1.1em}.mh-card__detail ::ng-deep b,.mh-card__detail ::ng-deep strong{font-weight:700}.mh-card__thumb{flex-shrink:0;width:56px;align-self:flex-start}.mh-card__thumb-btn,.mh-card__thumb-lock{display:flex;align-items:center;justify-content:center;width:56px;height:56px;padding:0;border:1px solid var(--border, #e5e7eb);background:var(--surface-2, #f1f5f9);cursor:pointer;overflow:hidden}.mh-card__thumb-btn{font:inherit;color:var(--muted)}.mh-card__thumb-btn:focus-visible{outline:2px solid var(--primary, #5b6cff);outline-offset:2px}.mh-card__thumb-lock{font-size:14px;color:var(--muted-2, #94a3b8)}.mh-card__thumb-img{display:block;width:100%;height:100%;object-fit:cover}.mh-card__actions{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:8px;border-top:1px solid color-mix(in srgb,var(--border, #e5e7eb) 70%,transparent)}.btn.ghost{border:1px solid var(--border);background:transparent}.btn.ghost.sm{padding:5px 10px;font-size:11px;border-radius:0}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}\n"], dependencies: [{ kind: "component", type: DateChipComponent, selector: "app-date-chip", inputs: ["date", "size", "interactive"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: SafeRichHtmlPipe, name: "safeRichHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11485
11679
  }
11486
11680
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: MedicalHistoryTimelineComponent, decorators: [{
11487
11681
  type: Component,
11488
- args: [{ selector: 'app-medical-history-timeline', standalone: true, imports: [DateChipComponent, NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mh-timeline\">\n @for (g of timelineGroups(); track g) {\n <section class=\"mh-month\">\n <header class=\"mh-month__header\">\n <span class=\"mh-month__dot\" aria-hidden=\"true\"></span>\n <h3 class=\"mh-month__title\">{{ g.monthLabel }}</h3>\n <span class=\"mh-month__line\" aria-hidden=\"true\"></span>\n <span class=\"mh-month__count\">{{ g.entries.length }}</span>\n </header>\n <ul class=\"mh-month__entries\">\n @for (e of g.entries; track e) {\n <li\n class=\"mh-card\"\n role=\"button\"\n (click)=\"onViewEntry(e)\"\n tabindex=\"0\"\n (keydown.enter)=\"onViewEntry(e)\"\n >\n <div class=\"mh-card__body\">\n <app-date-chip class=\"mh-card__date\" [date]=\"e.date\" />\n <div class=\"mh-card__main\">\n <div class=\"mh-card__top\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(e.kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(e.kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(e.kind).shortLabel }}\n </span>\n </div>\n <h4 class=\"mh-card__title\">{{ e.title }}</h4>\n @if (e.detail) {\n <p class=\"mh-card__detail\">{{ e.detail }}</p>\n }\n @if (e.place) {\n <p class=\"mh-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>{{ e.place }}\n </p>\n }\n <div class=\"mh-card__actions\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"btn ghost sm\"\n (click)=\"$event.stopPropagation(); editEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n @if (attachmentFor(e) && explorerEnabled()) {\n <button type=\"button\" class=\"btn ghost sm\" (click)=\"$event.stopPropagation(); onOpenPreview(e)\">\n <i class=\"pi pi-image\"></i> \u1EA2nh\n </button>\n }\n <button\n type=\"button\"\n class=\"btn ghost sm danger\"\n (click)=\"$event.stopPropagation(); deleteEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-trash\"></i>\n </button>\n </div>\n </div>\n @if (explorerEnabled()) {\n @if (attachmentFor(e); as att) {\n <div class=\"mh-card__thumb\" (click)=\"$event.stopPropagation()\">\n @if (att.content || att.previewUrl || att.driveFileId) {\n <button\n type=\"button\"\n class=\"mh-card__thumb-btn\"\n (click)=\"onOpenPreview(e)\"\n aria-label=\"Xem \u1EA3nh \u0111\u00EDnh k\u00E8m\"\n >\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <img\n class=\"mh-card__thumb-img\"\n [src]=\"att.previewUrl || att.content || placeholderSvg\"\n alt=\"\"\n (error)=\"onImageError($event, att)\"\n />\n }\n </button>\n }\n </div>\n }\n }\n </div>\n <span\n class=\"mh-card__accent\"\n [style.--k]=\"metaFor(e.kind).color\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n }\n</div>\n", styles: [".mh-timeline{display:flex;flex-direction:column;gap:24px;margin-top:4px;padding-bottom:8px}.mh-month__header{display:flex;align-items:center;gap:10px;margin-bottom:10px;padding:0 2px}.mh-month__dot{width:8px;height:8px;flex-shrink:0;border-radius:50%;background:var(--primary, #5b6cff)}.mh-month__title{margin:0;font-size:14px;font-weight:800;letter-spacing:-.02em;color:var(--text);white-space:nowrap}.mh-month__line{flex:1;height:1px;background:color-mix(in srgb,var(--border, #e5e7eb) 85%,transparent)}.mh-month__count{flex-shrink:0;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--muted, #64748b)}.mh-month__entries{list-style:none;margin:0;padding:0 0 0 14px;border-left:2px solid color-mix(in srgb,var(--border, #e5e7eb) 90%,transparent);display:flex;flex-direction:column;gap:10px}.mh-card{position:relative;display:block;width:100%;box-sizing:border-box;border-radius:0;overflow:hidden;border:1px solid color-mix(in srgb,var(--border, #e5e7eb) 88%,var(--primary, #5b6cff));background:var(--surface, #fff);cursor:pointer;transition:border-color .2s ease}.mh-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 35%,var(--border, #e5e7eb))}.mh-card:focus-visible{outline:2px solid color-mix(in srgb,var(--primary, #5b6cff) 55%,white);outline-offset:2px}.mh-card__accent{position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--k, #64748b)}.mh-card__body{display:flex;align-items:flex-start;gap:12px;padding:12px 12px 12px 14px;min-width:0}.mh-card__main{flex:1;min-width:0}.mh-card__top{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:6px 10px;margin-bottom:8px}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;background:color-mix(in srgb,var(--k, #64748b) 14%,white);color:var(--text)}.mh-kind-badge i{font-size:12px;color:var(--k, #64748b)}.mh-card__date{flex-shrink:0;margin-top:1px}.mh-card__title{margin:0 0 6px;font-size:15px;font-weight:800;line-height:1.3;letter-spacing:-.02em}.mh-card__detail{margin:0 0 6px;font-size:13px;line-height:1.45;color:var(--text);white-space:pre-line}.mh-card__place{margin:0 0 8px;font-size:12px;color:var(--muted);display:flex;align-items:flex-start;gap:6px;line-height:1.4}.mh-card__place i{margin-top:2px;opacity:.85;flex-shrink:0}.mh-card__thumb{flex-shrink:0;width:56px;align-self:flex-start}.mh-card__thumb-btn,.mh-card__thumb-lock{display:flex;align-items:center;justify-content:center;width:56px;height:56px;padding:0;border:1px solid var(--border, #e5e7eb);background:var(--surface-2, #f1f5f9);cursor:pointer;overflow:hidden}.mh-card__thumb-btn{font:inherit;color:var(--muted)}.mh-card__thumb-btn:focus-visible{outline:2px solid var(--primary, #5b6cff);outline-offset:2px}.mh-card__thumb-lock{font-size:14px;color:var(--muted-2, #94a3b8)}.mh-card__thumb-img{display:block;width:100%;height:100%;object-fit:cover}.mh-card__actions{display:flex;gap:6px;flex-wrap:wrap;padding-top:6px;border-top:1px solid color-mix(in srgb,var(--border, #e5e7eb) 85%,transparent);margin-top:2px}.btn.ghost{border:1px solid var(--border);background:transparent}.btn.ghost.sm{padding:5px 10px;font-size:11px;border-radius:0}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}\n"] }]
11682
+ args: [{ selector: 'app-medical-history-timeline', standalone: true, imports: [DateChipComponent, NgClass, SafeRichHtmlPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mh-timeline\">\n @for (g of timelineGroups(); track g) {\n <section class=\"mh-month\">\n <header class=\"mh-month__header\">\n <span class=\"mh-month__dot\" aria-hidden=\"true\"></span>\n <h3 class=\"mh-month__title\">{{ g.monthLabel }}</h3>\n <span class=\"mh-month__line\" aria-hidden=\"true\"></span>\n <span class=\"mh-month__count\">{{ g.entries.length }}</span>\n </header>\n <ul class=\"mh-month__entries\">\n @for (e of g.entries; track e) {\n <li\n class=\"mh-card\"\n role=\"button\"\n (click)=\"onViewEntry(e)\"\n tabindex=\"0\"\n (keydown.enter)=\"onViewEntry(e)\"\n >\n <div class=\"mh-card__body\">\n <div class=\"mh-card__row1\">\n <app-date-chip class=\"mh-card__date\" [date]=\"e.date\" />\n <div class=\"mh-card__main\">\n <div class=\"mh-card__top\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(e.kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(e.kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(e.kind).shortLabel }}\n </span>\n </div>\n <h4 class=\"mh-card__title\">{{ e.title }}</h4>\n @if (e.place) {\n <p class=\"mh-card__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>{{ e.place }}\n </p>\n }\n </div>\n @if (explorerEnabled()) {\n @if (attachmentFor(e); as att) {\n <div class=\"mh-card__thumb\" (click)=\"$event.stopPropagation()\">\n @if (att.content || att.previewUrl || att.driveFileId) {\n <button\n type=\"button\"\n class=\"mh-card__thumb-btn\"\n (click)=\"onOpenPreview(e)\"\n aria-label=\"Xem \u1EA3nh \u0111\u00EDnh k\u00E8m\"\n >\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <img\n class=\"mh-card__thumb-img\"\n [src]=\"att.previewUrl || att.content || placeholderSvg\"\n alt=\"\"\n (error)=\"onImageError($event, att)\"\n />\n }\n </button>\n }\n </div>\n }\n }\n </div>\n\n @if (e.detail) {\n <div class=\"mh-card__detail\" [innerHTML]=\"e.detail | safeRichHtml\"></div>\n }\n\n <div class=\"mh-card__actions\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"btn ghost sm danger\"\n (click)=\"$event.stopPropagation(); deleteEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-trash\"></i> Xo\u00E1\n </button>\n <button\n type=\"button\"\n class=\"btn ghost sm\"\n (click)=\"$event.stopPropagation(); editEntry.emit(e)\"\n [disabled]=\"!e.rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n </div>\n </div>\n <span\n class=\"mh-card__accent\"\n [style.--k]=\"metaFor(e.kind).color\"\n aria-hidden=\"true\"\n ></span>\n </li>\n }\n </ul>\n </section>\n }\n</div>\n", styles: [".mh-timeline{display:flex;flex-direction:column;gap:24px;margin-top:4px;padding-bottom:8px}.mh-month__header{display:flex;align-items:center;gap:10px;margin-bottom:10px;padding:0 2px}.mh-month__dot{width:8px;height:8px;flex-shrink:0;border-radius:50%;background:var(--primary, #5b6cff)}.mh-month__title{margin:0;font-size:14px;font-weight:800;letter-spacing:-.02em;color:var(--text);white-space:nowrap}.mh-month__line{flex:1;height:1px;background:color-mix(in srgb,var(--border, #e5e7eb) 85%,transparent)}.mh-month__count{flex-shrink:0;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--muted, #64748b)}.mh-month__entries{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}.mh-card{position:relative;display:block;width:100%;box-sizing:border-box;border-radius:0;overflow:hidden;border:1px solid color-mix(in srgb,var(--border, #e5e7eb) 88%,var(--primary, #5b6cff));background:var(--surface, #fff);cursor:pointer;transition:border-color .2s ease}.mh-card:hover{border-color:color-mix(in srgb,var(--primary, #5b6cff) 35%,var(--border, #e5e7eb))}.mh-card:focus-visible{outline:2px solid color-mix(in srgb,var(--primary, #5b6cff) 55%,white);outline-offset:2px}.mh-card__accent{position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--k, #64748b)}.mh-card__body{display:flex;flex-direction:column;gap:10px;padding:12px 12px 10px 14px;min-width:0}.mh-card__row1{display:flex;align-items:flex-start;gap:10px;min-width:0}.mh-card__date{flex-shrink:0}.mh-card__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.mh-card__top{display:flex;align-items:center}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:800;letter-spacing:.02em;background:color-mix(in srgb,var(--k, #64748b) 14%,white);color:var(--text);white-space:nowrap}.mh-kind-badge i{font-size:12px;color:var(--k, #64748b)}.mh-card__title{margin:0;font-size:15px;font-weight:800;line-height:1.3;letter-spacing:-.02em;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.mh-card__place{margin:0;font-size:12px;color:var(--muted);display:flex;align-items:flex-start;gap:6px;line-height:1.4}.mh-card__place i{margin-top:2px;opacity:.85;flex-shrink:0}.mh-card__detail{margin:0;font-size:13px;line-height:1.45;color:var(--text);word-break:break-word;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}.mh-card__detail ::ng-deep p{margin:0;display:inline}.mh-card__detail ::ng-deep p:after{content:\" \"}.mh-card__detail ::ng-deep ul,.mh-card__detail ::ng-deep ol{margin:0;padding-left:1.1em}.mh-card__detail ::ng-deep b,.mh-card__detail ::ng-deep strong{font-weight:700}.mh-card__thumb{flex-shrink:0;width:56px;align-self:flex-start}.mh-card__thumb-btn,.mh-card__thumb-lock{display:flex;align-items:center;justify-content:center;width:56px;height:56px;padding:0;border:1px solid var(--border, #e5e7eb);background:var(--surface-2, #f1f5f9);cursor:pointer;overflow:hidden}.mh-card__thumb-btn{font:inherit;color:var(--muted)}.mh-card__thumb-btn:focus-visible{outline:2px solid var(--primary, #5b6cff);outline-offset:2px}.mh-card__thumb-lock{font-size:14px;color:var(--muted-2, #94a3b8)}.mh-card__thumb-img{display:block;width:100%;height:100%;object-fit:cover}.mh-card__actions{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:8px;border-top:1px solid color-mix(in srgb,var(--border, #e5e7eb) 70%,transparent)}.btn.ghost{border:1px solid var(--border);background:transparent}.btn.ghost.sm{padding:5px 10px;font-size:11px;border-radius:0}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}\n"] }]
11489
11683
  }], propDecorators: { timelineGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelineGroups", required: false }] }], resolveAttachment: [{ type: i0.Input, args: [{ isSignal: true, alias: "resolveAttachment", required: false }] }], explorerEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "explorerEnabled", required: false }] }], editEntry: [{ type: i0.Output, args: ["editEntry"] }], deleteEntry: [{ type: i0.Output, args: ["deleteEntry"] }], viewEntry: [{ type: i0.Output, args: ["viewEntry"] }], openPreview: [{ type: i0.Output, args: ["openPreview"] }], openInDocuments: [{ type: i0.Output, args: ["openInDocuments"] }] } });
11490
11684
 
11491
11685
  class MedicalHistoryPreviewComponent {
@@ -11746,11 +11940,11 @@ class MedicalHistoryDetailDialogComponent {
11746
11940
  this.openPreview.emit(this.entry());
11747
11941
  }
11748
11942
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: MedicalHistoryDetailDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
11749
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: MedicalHistoryDetailDialogComponent, isStandalone: true, selector: "app-medical-history-detail-dialog", inputs: { entry: { classPropertyName: "entry", publicName: "entry", isSignal: true, isRequired: true, transformFunction: null }, attachment: { classPropertyName: "attachment", publicName: "attachment", isSignal: true, isRequired: false, transformFunction: null }, explorerEnabled: { classPropertyName: "explorerEnabled", publicName: "explorerEnabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", edit: "edit", delete: "delete", openPreview: "openPreview" }, ngImport: i0, template: "<div class=\"dialog-overlay\" (click)=\"onClose()\">\n <div\n class=\"dialog mh-detail-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'Chi ti\u1EBFt: ' + entry().title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head mh-detail-dialog__head\">\n <app-date-chip class=\"mh-detail-dialog__date-chip\" [date]=\"entry().date\" size=\"md\" />\n <div class=\"mh-detail-dialog__title-wrap\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(entry().kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(entry().kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(entry().kind).label }}\n </span>\n <h2 class=\"mh-detail-dialog__title\">{{ entry().title }}</h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"onClose()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n\n <div class=\"mh-detail-dialog__body\">\n @if (entry().detail) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">Chi ti\u1EBFt</h3>\n <p class=\"mh-detail-dialog__text\">{{ entry().detail }}</p>\n </section>\n }\n\n @if (entry().place) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">N\u01A1i kh\u00E1m</h3>\n <p class=\"mh-detail-dialog__text mh-detail-dialog__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n {{ entry().place }}\n </p>\n </section>\n }\n\n @if (attachment(); as att) {\n @if (explorerEnabled()) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</h3>\n <button type=\"button\" class=\"mh-detail-dialog__thumb\" (click)=\"onOpenPreview()\">\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else if (att.previewUrl || att.content) {\n <img [src]=\"att.previewUrl || att.content\" alt=\"\u1EA2nh \u0111\u00EDnh k\u00E8m\" />\n } @else {\n <i class=\"pi pi-image\" aria-hidden=\"true\"></i>\n <span>Xem \u1EA3nh</span>\n }\n </button>\n </section>\n }\n }\n </div>\n\n <div class=\"dialog-actions mh-detail-dialog__actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"onClose()\">\u0110\u00F3ng</button>\n @if (attachment() && explorerEnabled()) {\n <button type=\"button\" class=\"btn ghost\" (click)=\"onOpenPreview()\">\n <i class=\"pi pi-image\"></i> Xem \u1EA3nh\n </button>\n }\n <button\n type=\"button\"\n class=\"btn ghost danger\"\n (click)=\"onDelete()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-trash\"></i> Xo\u00E1\n </button>\n <button\n type=\"button\"\n class=\"btn primary\"\n (click)=\"onEdit()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n </div>\n </div>\n</div>\n", styles: [":host{--surface: #ffffff;--surface-2: #f8fafc;--border: #e6e8ef;--primary: #5b6cff;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;display:block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.dialog{width:100%;background:var(--surface);box-shadow:0 24px 48px -24px #0f172a59}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.dialog-close{flex-shrink:0;border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px;font-size:16px;line-height:1}.dialog-close:hover{background:var(--surface-2)}.dialog-actions{display:flex;justify-content:flex-end;align-items:center;gap:10px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:14px;font-weight:700;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent}.btn:disabled{opacity:.55;cursor:not-allowed}.btn.ghost{border:1px solid var(--border);background:transparent;color:var(--text);padding:10px 16px;border-radius:12px}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}.btn.primary{background:var(--primary);color:#fff;border:none;padding:10px 16px;border-radius:12px}.mh-detail-dialog{max-width:min(520px,100vw - 24px);max-height:min(100dvh - 32px,640px);overflow:hidden;display:flex;flex-direction:column;padding:0;border-radius:16px}.mh-detail-dialog__head{display:flex;align-items:flex-start;gap:12px;padding:16px 16px 12px;margin:0;border-bottom:1px solid var(--border)}.mh-detail-dialog__date-chip{flex-shrink:0}.mh-detail-dialog__title-wrap{min-width:0;display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__title{margin:0;font-size:1.1rem;font-weight:800;line-height:1.3;color:var(--text);word-break:break-word}.mh-detail-dialog__body{flex:1;min-height:0;overflow-y:auto;padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mh-detail-dialog__section{display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__label{margin:0;font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}.mh-detail-dialog__text{margin:0;font-size:14px;line-height:1.55;color:var(--text);white-space:pre-wrap;word-break:break-word}.mh-detail-dialog__place{display:flex;align-items:flex-start;gap:6px}.mh-detail-dialog__place i{margin-top:2px;color:var(--primary)}.mh-detail-dialog__thumb{width:100%;max-width:220px;aspect-ratio:4/3;border:1px solid var(--border);border-radius:12px;overflow:hidden;background:var(--surface-2);display:inline-flex;align-items:center;justify-content:center;gap:8px;color:var(--muted);cursor:pointer;padding:0}.mh-detail-dialog__thumb img{width:100%;height:100%;object-fit:cover;display:block}.mh-detail-dialog__actions{padding:12px 16px 16px;border-top:1px solid var(--border);flex-wrap:wrap}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;width:fit-content;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;color:var(--k, var(--primary));background:color-mix(in srgb,var(--k, #5b6cff) 12%,white);border:1px solid color-mix(in srgb,var(--k, #5b6cff) 24%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: DateChipComponent, selector: "app-date-chip", inputs: ["date", "size", "interactive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11943
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: MedicalHistoryDetailDialogComponent, isStandalone: true, selector: "app-medical-history-detail-dialog", inputs: { entry: { classPropertyName: "entry", publicName: "entry", isSignal: true, isRequired: true, transformFunction: null }, attachment: { classPropertyName: "attachment", publicName: "attachment", isSignal: true, isRequired: false, transformFunction: null }, explorerEnabled: { classPropertyName: "explorerEnabled", publicName: "explorerEnabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", edit: "edit", delete: "delete", openPreview: "openPreview" }, ngImport: i0, template: "<div class=\"dialog-overlay\" (click)=\"onClose()\">\n <div\n class=\"dialog mh-detail-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'Chi ti\u1EBFt: ' + entry().title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head mh-detail-dialog__head\">\n <app-date-chip class=\"mh-detail-dialog__date-chip\" [date]=\"entry().date\" size=\"md\" />\n <div class=\"mh-detail-dialog__title-wrap\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(entry().kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(entry().kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(entry().kind).label }}\n </span>\n <h2 class=\"mh-detail-dialog__title\">{{ entry().title }}</h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"onClose()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n\n <div class=\"mh-detail-dialog__body\">\n @if (entry().detail) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">Chi ti\u1EBFt</h3>\n <div class=\"mh-detail-dialog__text\" [innerHTML]=\"entry().detail | safeRichHtml\"></div>\n </section>\n }\n\n @if (entry().place) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">N\u01A1i kh\u00E1m</h3>\n <p class=\"mh-detail-dialog__text mh-detail-dialog__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n {{ entry().place }}\n </p>\n </section>\n }\n\n @if (attachment(); as att) {\n @if (explorerEnabled()) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</h3>\n <button type=\"button\" class=\"mh-detail-dialog__thumb\" (click)=\"onOpenPreview()\">\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else if (att.previewUrl || att.content) {\n <img [src]=\"att.previewUrl || att.content\" alt=\"\u1EA2nh \u0111\u00EDnh k\u00E8m\" />\n } @else {\n <i class=\"pi pi-image\" aria-hidden=\"true\"></i>\n <span>Xem \u1EA3nh</span>\n }\n </button>\n </section>\n }\n }\n </div>\n\n <div class=\"dialog-actions mh-detail-dialog__actions\">\n <div class=\"mh-detail-dialog__actions-start\">\n <button\n type=\"button\"\n class=\"btn ghost danger\"\n (click)=\"onDelete()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-trash\"></i> Xo\u00E1\n </button>\n <button\n type=\"button\"\n class=\"btn primary\"\n (click)=\"onEdit()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n </div>\n <button type=\"button\" class=\"btn ghost\" (click)=\"onClose()\">\u0110\u00F3ng</button>\n </div>\n </div>\n</div>\n", styles: [":host{--surface: #ffffff;--surface-2: #f8fafc;--border: #e6e8ef;--primary: #5b6cff;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;display:block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.dialog{width:100%;background:var(--surface);box-shadow:0 24px 48px -24px #0f172a59}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.dialog-close{flex-shrink:0;border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px;font-size:16px;line-height:1}.dialog-close:hover{background:var(--surface-2)}.dialog-actions{display:flex;justify-content:flex-end;align-items:center;gap:10px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:14px;font-weight:700;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent}.btn:disabled{opacity:.55;cursor:not-allowed}.btn.ghost{border:1px solid var(--border);background:transparent;color:var(--text);padding:10px 16px;border-radius:12px}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}.btn.primary{background:var(--primary);color:#fff;border:none;padding:10px 16px;border-radius:12px}.mh-detail-dialog{max-width:min(520px,100vw - 24px);max-height:min(100dvh - 32px,640px);overflow:hidden;display:flex;flex-direction:column;padding:0;border-radius:16px}@media(max-width:639px){.mh-detail-dialog{max-height:min(100dvh - 88px,640px)}}.mh-detail-dialog__head{flex-shrink:0;display:flex;align-items:flex-start;gap:12px;padding:16px 16px 12px;margin:0;border-bottom:1px solid var(--border)}.mh-detail-dialog__date-chip{flex-shrink:0}.mh-detail-dialog__title-wrap{min-width:0;display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__title{margin:0;font-size:1.1rem;font-weight:800;line-height:1.3;color:var(--text);word-break:break-word}.mh-detail-dialog__body{flex:1;min-height:0;overflow-y:auto;padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mh-detail-dialog__section{display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__label{margin:0;font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}.mh-detail-dialog__text{margin:0;font-size:14px;line-height:1.55;color:var(--text);word-break:break-word}.mh-detail-dialog__text ::ng-deep p{margin:0 0 .45em}.mh-detail-dialog__text ::ng-deep p:last-child{margin-bottom:0}.mh-detail-dialog__text ::ng-deep ul,.mh-detail-dialog__text ::ng-deep ol{margin:.35em 0;padding-left:1.35em}.mh-detail-dialog__text ::ng-deep b,.mh-detail-dialog__text ::ng-deep strong{font-weight:700}.mh-detail-dialog__place{display:flex;align-items:flex-start;gap:6px}.mh-detail-dialog__place i{margin-top:2px;color:var(--primary)}.mh-detail-dialog__thumb{width:100%;max-width:220px;aspect-ratio:4/3;border:1px solid var(--border);border-radius:12px;overflow:hidden;background:var(--surface-2);display:inline-flex;align-items:center;justify-content:center;gap:8px;color:var(--muted);cursor:pointer;padding:0}.mh-detail-dialog__thumb img{width:100%;height:100%;object-fit:cover;display:block}.mh-detail-dialog__actions{flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 16px 16px;border-top:1px solid var(--border);background:var(--surface)}.mh-detail-dialog__actions-start{display:flex;align-items:center;gap:10px;min-width:0}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;width:fit-content;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;color:var(--k, var(--primary));background:color-mix(in srgb,var(--k, #5b6cff) 12%,white);border:1px solid color-mix(in srgb,var(--k, #5b6cff) 24%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: DateChipComponent, selector: "app-date-chip", inputs: ["date", "size", "interactive"] }, { kind: "pipe", type: SafeRichHtmlPipe, name: "safeRichHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11750
11944
  }
11751
11945
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: MedicalHistoryDetailDialogComponent, decorators: [{
11752
11946
  type: Component,
11753
- args: [{ selector: 'app-medical-history-detail-dialog', standalone: true, imports: [CommonModule, DateChipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"dialog-overlay\" (click)=\"onClose()\">\n <div\n class=\"dialog mh-detail-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'Chi ti\u1EBFt: ' + entry().title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head mh-detail-dialog__head\">\n <app-date-chip class=\"mh-detail-dialog__date-chip\" [date]=\"entry().date\" size=\"md\" />\n <div class=\"mh-detail-dialog__title-wrap\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(entry().kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(entry().kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(entry().kind).label }}\n </span>\n <h2 class=\"mh-detail-dialog__title\">{{ entry().title }}</h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"onClose()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n\n <div class=\"mh-detail-dialog__body\">\n @if (entry().detail) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">Chi ti\u1EBFt</h3>\n <p class=\"mh-detail-dialog__text\">{{ entry().detail }}</p>\n </section>\n }\n\n @if (entry().place) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">N\u01A1i kh\u00E1m</h3>\n <p class=\"mh-detail-dialog__text mh-detail-dialog__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n {{ entry().place }}\n </p>\n </section>\n }\n\n @if (attachment(); as att) {\n @if (explorerEnabled()) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</h3>\n <button type=\"button\" class=\"mh-detail-dialog__thumb\" (click)=\"onOpenPreview()\">\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else if (att.previewUrl || att.content) {\n <img [src]=\"att.previewUrl || att.content\" alt=\"\u1EA2nh \u0111\u00EDnh k\u00E8m\" />\n } @else {\n <i class=\"pi pi-image\" aria-hidden=\"true\"></i>\n <span>Xem \u1EA3nh</span>\n }\n </button>\n </section>\n }\n }\n </div>\n\n <div class=\"dialog-actions mh-detail-dialog__actions\">\n <button type=\"button\" class=\"btn ghost\" (click)=\"onClose()\">\u0110\u00F3ng</button>\n @if (attachment() && explorerEnabled()) {\n <button type=\"button\" class=\"btn ghost\" (click)=\"onOpenPreview()\">\n <i class=\"pi pi-image\"></i> Xem \u1EA3nh\n </button>\n }\n <button\n type=\"button\"\n class=\"btn ghost danger\"\n (click)=\"onDelete()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-trash\"></i> Xo\u00E1\n </button>\n <button\n type=\"button\"\n class=\"btn primary\"\n (click)=\"onEdit()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n </div>\n </div>\n</div>\n", styles: [":host{--surface: #ffffff;--surface-2: #f8fafc;--border: #e6e8ef;--primary: #5b6cff;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;display:block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.dialog{width:100%;background:var(--surface);box-shadow:0 24px 48px -24px #0f172a59}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.dialog-close{flex-shrink:0;border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px;font-size:16px;line-height:1}.dialog-close:hover{background:var(--surface-2)}.dialog-actions{display:flex;justify-content:flex-end;align-items:center;gap:10px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:14px;font-weight:700;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent}.btn:disabled{opacity:.55;cursor:not-allowed}.btn.ghost{border:1px solid var(--border);background:transparent;color:var(--text);padding:10px 16px;border-radius:12px}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}.btn.primary{background:var(--primary);color:#fff;border:none;padding:10px 16px;border-radius:12px}.mh-detail-dialog{max-width:min(520px,100vw - 24px);max-height:min(100dvh - 32px,640px);overflow:hidden;display:flex;flex-direction:column;padding:0;border-radius:16px}.mh-detail-dialog__head{display:flex;align-items:flex-start;gap:12px;padding:16px 16px 12px;margin:0;border-bottom:1px solid var(--border)}.mh-detail-dialog__date-chip{flex-shrink:0}.mh-detail-dialog__title-wrap{min-width:0;display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__title{margin:0;font-size:1.1rem;font-weight:800;line-height:1.3;color:var(--text);word-break:break-word}.mh-detail-dialog__body{flex:1;min-height:0;overflow-y:auto;padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mh-detail-dialog__section{display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__label{margin:0;font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}.mh-detail-dialog__text{margin:0;font-size:14px;line-height:1.55;color:var(--text);white-space:pre-wrap;word-break:break-word}.mh-detail-dialog__place{display:flex;align-items:flex-start;gap:6px}.mh-detail-dialog__place i{margin-top:2px;color:var(--primary)}.mh-detail-dialog__thumb{width:100%;max-width:220px;aspect-ratio:4/3;border:1px solid var(--border);border-radius:12px;overflow:hidden;background:var(--surface-2);display:inline-flex;align-items:center;justify-content:center;gap:8px;color:var(--muted);cursor:pointer;padding:0}.mh-detail-dialog__thumb img{width:100%;height:100%;object-fit:cover;display:block}.mh-detail-dialog__actions{padding:12px 16px 16px;border-top:1px solid var(--border);flex-wrap:wrap}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;width:fit-content;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;color:var(--k, var(--primary));background:color-mix(in srgb,var(--k, #5b6cff) 12%,white);border:1px solid color-mix(in srgb,var(--k, #5b6cff) 24%,transparent)}\n"] }]
11947
+ args: [{ selector: 'app-medical-history-detail-dialog', standalone: true, imports: [CommonModule, DateChipComponent, SafeRichHtmlPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"dialog-overlay\" (click)=\"onClose()\">\n <div\n class=\"dialog mh-detail-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'Chi ti\u1EBFt: ' + entry().title\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"dialog-head mh-detail-dialog__head\">\n <app-date-chip class=\"mh-detail-dialog__date-chip\" [date]=\"entry().date\" size=\"md\" />\n <div class=\"mh-detail-dialog__title-wrap\">\n <span class=\"mh-kind-badge\" [style.--k]=\"metaFor(entry().kind).color\">\n <i class=\"fa-solid\" [ngClass]=\"metaFor(entry().kind).icon\" aria-hidden=\"true\"></i>\n {{ metaFor(entry().kind).label }}\n </span>\n <h2 class=\"mh-detail-dialog__title\">{{ entry().title }}</h2>\n </div>\n <button type=\"button\" class=\"dialog-close\" (click)=\"onClose()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n\n <div class=\"mh-detail-dialog__body\">\n @if (entry().detail) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">Chi ti\u1EBFt</h3>\n <div class=\"mh-detail-dialog__text\" [innerHTML]=\"entry().detail | safeRichHtml\"></div>\n </section>\n }\n\n @if (entry().place) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">N\u01A1i kh\u00E1m</h3>\n <p class=\"mh-detail-dialog__text mh-detail-dialog__place\">\n <i class=\"pi pi-map-marker\" aria-hidden=\"true\"></i>\n {{ entry().place }}\n </p>\n </section>\n }\n\n @if (attachment(); as att) {\n @if (explorerEnabled()) {\n <section class=\"mh-detail-dialog__section\">\n <h3 class=\"mh-detail-dialog__label\">\u1EA2nh \u0111\u00EDnh k\u00E8m</h3>\n <button type=\"button\" class=\"mh-detail-dialog__thumb\" (click)=\"onOpenPreview()\">\n @if (att.isLoading) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else if (att.previewUrl || att.content) {\n <img [src]=\"att.previewUrl || att.content\" alt=\"\u1EA2nh \u0111\u00EDnh k\u00E8m\" />\n } @else {\n <i class=\"pi pi-image\" aria-hidden=\"true\"></i>\n <span>Xem \u1EA3nh</span>\n }\n </button>\n </section>\n }\n }\n </div>\n\n <div class=\"dialog-actions mh-detail-dialog__actions\">\n <div class=\"mh-detail-dialog__actions-start\">\n <button\n type=\"button\"\n class=\"btn ghost danger\"\n (click)=\"onDelete()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-trash\"></i> Xo\u00E1\n </button>\n <button\n type=\"button\"\n class=\"btn primary\"\n (click)=\"onEdit()\"\n [disabled]=\"!entry().rowIndex\"\n >\n <i class=\"pi pi-pencil\"></i> S\u1EEDa\n </button>\n </div>\n <button type=\"button\" class=\"btn ghost\" (click)=\"onClose()\">\u0110\u00F3ng</button>\n </div>\n </div>\n</div>\n", styles: [":host{--surface: #ffffff;--surface-2: #f8fafc;--border: #e6e8ef;--primary: #5b6cff;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;display:block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.dialog-overlay{position:fixed;inset:0;z-index:250;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:max(16px,env(safe-area-inset-top,0px));padding-bottom:max(16px,env(safe-area-inset-bottom,0px));box-sizing:border-box}@media(max-width:639px){.dialog-overlay{padding-bottom:calc(68px + env(safe-area-inset-bottom,0px));align-items:center}}.dialog{width:100%;background:var(--surface);box-shadow:0 24px 48px -24px #0f172a59}.dialog-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}.dialog-close{flex-shrink:0;border:none;background:transparent;padding:8px;cursor:pointer;color:var(--muted);border-radius:10px;font-size:16px;line-height:1}.dialog-close:hover{background:var(--surface-2)}.dialog-actions{display:flex;justify-content:flex-end;align-items:center;gap:10px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:14px;font-weight:700;font-family:inherit;cursor:pointer;-webkit-tap-highlight-color:transparent}.btn:disabled{opacity:.55;cursor:not-allowed}.btn.ghost{border:1px solid var(--border);background:transparent;color:var(--text);padding:10px 16px;border-radius:12px}.btn.ghost.danger{color:#b91c1c;border-color:#fecaca}.btn.primary{background:var(--primary);color:#fff;border:none;padding:10px 16px;border-radius:12px}.mh-detail-dialog{max-width:min(520px,100vw - 24px);max-height:min(100dvh - 32px,640px);overflow:hidden;display:flex;flex-direction:column;padding:0;border-radius:16px}@media(max-width:639px){.mh-detail-dialog{max-height:min(100dvh - 88px,640px)}}.mh-detail-dialog__head{flex-shrink:0;display:flex;align-items:flex-start;gap:12px;padding:16px 16px 12px;margin:0;border-bottom:1px solid var(--border)}.mh-detail-dialog__date-chip{flex-shrink:0}.mh-detail-dialog__title-wrap{min-width:0;display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__title{margin:0;font-size:1.1rem;font-weight:800;line-height:1.3;color:var(--text);word-break:break-word}.mh-detail-dialog__body{flex:1;min-height:0;overflow-y:auto;padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mh-detail-dialog__section{display:flex;flex-direction:column;gap:6px}.mh-detail-dialog__label{margin:0;font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--muted-2)}.mh-detail-dialog__text{margin:0;font-size:14px;line-height:1.55;color:var(--text);word-break:break-word}.mh-detail-dialog__text ::ng-deep p{margin:0 0 .45em}.mh-detail-dialog__text ::ng-deep p:last-child{margin-bottom:0}.mh-detail-dialog__text ::ng-deep ul,.mh-detail-dialog__text ::ng-deep ol{margin:.35em 0;padding-left:1.35em}.mh-detail-dialog__text ::ng-deep b,.mh-detail-dialog__text ::ng-deep strong{font-weight:700}.mh-detail-dialog__place{display:flex;align-items:flex-start;gap:6px}.mh-detail-dialog__place i{margin-top:2px;color:var(--primary)}.mh-detail-dialog__thumb{width:100%;max-width:220px;aspect-ratio:4/3;border:1px solid var(--border);border-radius:12px;overflow:hidden;background:var(--surface-2);display:inline-flex;align-items:center;justify-content:center;gap:8px;color:var(--muted);cursor:pointer;padding:0}.mh-detail-dialog__thumb img{width:100%;height:100%;object-fit:cover;display:block}.mh-detail-dialog__actions{flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 16px 16px;border-top:1px solid var(--border);background:var(--surface)}.mh-detail-dialog__actions-start{display:flex;align-items:center;gap:10px;min-width:0}.mh-kind-badge{display:inline-flex;align-items:center;gap:6px;width:fit-content;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;color:var(--k, var(--primary));background:color-mix(in srgb,var(--k, #5b6cff) 12%,white);border:1px solid color-mix(in srgb,var(--k, #5b6cff) 24%,transparent)}\n"] }]
11754
11948
  }], propDecorators: { entry: [{ type: i0.Input, args: [{ isSignal: true, alias: "entry", required: true }] }], attachment: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachment", required: false }] }], explorerEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "explorerEnabled", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], edit: [{ type: i0.Output, args: ["edit"] }], delete: [{ type: i0.Output, args: ["delete"] }], openPreview: [{ type: i0.Output, args: ["openPreview"] }] } });
11755
11949
 
11756
11950
  const EXPLORER_ROOT_ID = 1;
@@ -12209,7 +12403,7 @@ class MedicalHistoryComponent {
12209
12403
  entryMatchesFreeText(e, query) {
12210
12404
  const m = kindMeta(e.kind);
12211
12405
  return (viFoldIncludes(e.title, query) ||
12212
- viFoldIncludes(e.detail, query) ||
12406
+ viFoldIncludes(stripHtml(e.detail), query) ||
12213
12407
  viFoldIncludes(e.place || '', query) ||
12214
12408
  viFoldIncludes(m.label, query) ||
12215
12409
  viFoldIncludes(m.shortLabel, query) ||
@@ -21114,66 +21308,13 @@ class DailySummaryComponent {
21114
21308
  });
21115
21309
  }
21116
21310
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DailySummaryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21117
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DailySummaryComponent, isStandalone: true, selector: "app-daily-summary", inputs: { logs: { classPropertyName: "logs", publicName: "logs", isSignal: true, isRequired: false, transformFunction: null }, birthDate: { classPropertyName: "birthDate", publicName: "birthDate", isSignal: true, isRequired: false, transformFunction: null }, initialDate: { classPropertyName: "initialDate", publicName: "initialDate", isSignal: true, isRequired: false, transformFunction: null }, feedGroupGapMinutes: { classPropertyName: "feedGroupGapMinutes", publicName: "feedGroupGapMinutes", isSignal: true, isRequired: false, transformFunction: null }, gender: { classPropertyName: "gender", publicName: "gender", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let s = summary();\n\n<div class=\"ds\">\n <!-- \u0110i\u1EC1u h\u01B0\u1EDBng ng\u00E0y -->\n <div class=\"ds-datebar\" [class.ds-datebar--weekend]=\"s.weekend\">\n <button type=\"button\" class=\"ds-datebar__nav\" (click)=\"shiftDay(-1)\" aria-label=\"Ng\u00E0y tr\u01B0\u1EDBc\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <label class=\"ds-datebar__center\">\n <span class=\"ds-datebar__label\">\n {{ s.weekday }}, {{ s.displayDate }}\n </span>\n @if (s.weekend) {\n <span class=\"ds-datebar__badge\">Cu\u1ED1i tu\u1EA7n</span>\n }\n <input\n type=\"date\"\n class=\"ds-datebar__input\"\n [value]=\"s.date\"\n [max]=\"maxDate\"\n (change)=\"onDateInput($any($event.target).value)\"\n aria-label=\"Ch\u1ECDn ng\u00E0y\"\n />\n </label>\n\n <button\n type=\"button\"\n class=\"ds-datebar__nav\"\n [disabled]=\"!canGoForward()\"\n (click)=\"shiftDay(1)\"\n aria-label=\"Ng\u00E0y sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @if (canGoForward()) {\n <button type=\"button\" class=\"ds-reset\" (click)=\"goLatest()\">\n <i class=\"pi pi-replay\" aria-hidden=\"true\"></i>\n V\u1EC1 ng\u00E0y m\u1EDBi nh\u1EA5t ({{ maxDateLabel }})\n </button>\n }\n\n <!-- T\u1ED5ng quan -->\n <section class=\"ds-hero\" aria-label=\"T\u1ED5ng quan trong ng\u00E0y\">\n <div class=\"ds-hero__top\">\n <div>\n <div class=\"ds-hero__label\">T\u1ED4NG L\u01AF\u1EE2NG S\u1EEEA TRONG NG\u00C0Y</div>\n <div class=\"ds-hero__total\">{{ s.totalMl }}<span class=\"ds-hero__unit\">ml</span></div>\n @if (s.hasFeeding || s.yesterday.totalMl > 0) {\n <div\n class=\"ds-hero__delta\"\n [class.ds-hero__delta--up]=\"s.yesterday.deltaMl > 0\"\n [class.ds-hero__delta--down]=\"s.yesterday.deltaMl < 0\"\n [class.ds-hero__delta--flat]=\"s.yesterday.deltaMl === 0\"\n >\n @if (s.yesterday.totalMl === 0 && s.hasFeeding) {\n H\u00F4m qua ch\u01B0a ghi nh\u1EADn c\u1EEF\n } @else if (s.yesterday.deltaMl === 0) {\n B\u1EB1ng h\u00F4m qua ({{ s.yesterday.totalMl }}ml)\n } @else {\n <i\n class=\"pi\"\n [class.pi-arrow-up]=\"s.yesterday.deltaMl > 0\"\n [class.pi-arrow-down]=\"s.yesterday.deltaMl < 0\"\n aria-hidden=\"true\"\n ></i>\n {{ s.yesterday.deltaMl > 0 ? '+' : '' }}{{ s.yesterday.deltaMl }}ml\n @if (s.yesterday.deltaPercent !== null) {\n <span class=\"ds-hero__delta-pct\">\n ({{ s.yesterday.deltaPercent > 0 ? '+' : '' }}{{ s.yesterday.deltaPercent }}%)\n </span>\n }\n so v\u1EDBi h\u00F4m qua\n }\n </div>\n }\n </div>\n <div class=\"ds-hero__right\">\n <div class=\"ds-hero__count\">{{ s.feedCount }} c\u1EEF b\u00FA</div>\n <div class=\"ds-hero__avg\">\n @if (s.feedCount > 0) {\n trung b\u00ECnh {{ s.avgMl }}ml m\u1ED7i c\u1EEF\n } @else {\n ch\u01B0a ghi nh\u1EADn c\u1EEF n\u00E0o\n }\n </div>\n </div>\n </div>\n\n <div class=\"ds-hero__stats\">\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF cao nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--green\">\n {{ s.hasFeeding ? s.maxMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF th\u1EA5p nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--blue\">\n {{ s.hasFeeding ? s.minMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">B\u00FA \u0111\u00EAm</span>\n <span\n class=\"ds-hero__stat-value\"\n [class.ds-hero__stat-value--indigo]=\"s.nightCount > 0\"\n [class.ds-hero__stat-value--green]=\"s.nightCount === 0\"\n >\n {{ s.nightCount > 0 ? s.nightCount + ' c\u1EEF' : 'Kh\u00F4ng c\u00F3' }}\n </span>\n </div>\n </div>\n </section>\n\n @if (s.nightCount > 0) {\n <div class=\"ds-night\">\n B\u00E9 c\u00F3 b\u00FA \u0111\u00EAm trong khung 00:00 \u2013 05:00: {{ s.nightCount }} c\u1EEF b\u00FA, t\u1ED5ng {{ s.nightMl }}ml\n </div>\n }\n\n <!-- D\u1EA3i hai tu\u1EA7n -->\n <section class=\"ds-strip\" aria-label=\"D\u1EA3i hai tu\u1EA7n\">\n <div class=\"ds-strip__frame\">\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n (click)=\"shiftStripWeek(-2)\"\n aria-label=\"Tu\u1EA7n tr\u01B0\u1EDBc\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"ds-strip__content\">\n <div class=\"ds-strip__chart\" aria-hidden=\"true\">\n @for (cell of s.strip; track cell.date) {\n <div\n class=\"ds-strip__bar-col\"\n [class.ds-strip__bar-col--current]=\"cell.current\"\n [class.ds-strip__bar-col--weekend]=\"cell.weekend\"\n >\n <div class=\"ds-strip__bar-track\">\n <span\n class=\"ds-strip__bar\"\n [class.ds-strip__bar--empty]=\"!cell.hasFeeding\"\n [style.height.%]=\"cell.barPercent\"\n ></span>\n </div>\n @if (cell.hasFeeding) {\n <span class=\"ds-strip__bar-label\">{{ cell.totalMl }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"ds-strip__row\">\n @for (cell of s.strip; track cell.date) {\n <button\n type=\"button\"\n class=\"ds-strip__cell\"\n [class.ds-strip__cell--current]=\"cell.current\"\n [class.ds-strip__cell--weekend]=\"cell.weekend\"\n [disabled]=\"!isNavigable(cell.date)\"\n (click)=\"openDate(cell.date)\"\n [attr.aria-current]=\"cell.current ? 'date' : null\"\n [attr.aria-label]=\"cell.weekdayShort + ' ng\u00E0y ' + cell.dayNumber\"\n >\n <span class=\"ds-strip__dow\">{{ cell.weekdayShort }}</span>\n <span class=\"ds-strip__day\" [class.ds-strip__day--data]=\"cell.hasFeeding\">\n {{ cell.dayNumber }}\n </span>\n <span\n class=\"ds-strip__dot\"\n [class.ds-strip__dot--activity]=\"cell.hasActivity\"\n [class.ds-strip__dot--event]=\"!cell.hasActivity && cell.hasEvent\"\n ></span>\n </button>\n }\n </div>\n </div>\n\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n [disabled]=\"!canShiftStripForward()\"\n (click)=\"shiftStripWeek(2)\"\n aria-label=\"Tu\u1EA7n sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"ds-strip__legend\">\n <span><i class=\"ds-strip__dot ds-strip__dot--activity\"></i> C\u00F3 ho\u1EA1t \u0111\u1ED9ng</span>\n <span><i class=\"ds-strip__dot ds-strip__dot--event\"></i> C\u00F3 l\u1ECBch h\u1EB9n</span>\n </div>\n </section>\n\n <!-- Th\u1EC3 tr\u1EA1ng & m\u1EE9c \u0111\u1EA1t khuy\u1EBFn ngh\u1ECB -->\n @if (s.body) {\n <section class=\"ds-body-card\" aria-label=\"Th\u1EC3 tr\u1EA1ng v\u00E0 khuy\u1EBFn ngh\u1ECB\">\n <div class=\"ds-body-card__title\">Th\u1EC3 tr\u1EA1ng &amp; khuy\u1EBFn ngh\u1ECB</div>\n\n <div class=\"ds-growth\">\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.weightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n C\u00E2n n\u1EB7ng\n </span>\n @if (s.body.weightWho; as ww) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"ww.status\">\n {{ ww.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">{{ s.body.weightKg }}<span>kg</span></div>\n\n @if (s.body.weightWho; as ww) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ ww.actual }}kg</span>\n <span>Trung v\u1ECB WHO {{ ww.median }}kg</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"ww.status\"\n [style.left.%]=\"whoMarkerPosition(ww.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ ww.percentile }} \u00B7 z {{ formatWhoZ(ww.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"ww.status\">\n {{ ww.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n @if (s.body.ageInDays !== null) {\n Th\u00EAm gi\u1EDBi t\u00EDnh trong profile \u0111\u1EC3 so chu\u1EA9n WHO\n } @else {\n C\u1EA7n ng\u00E0y sinh \u0111\u1EC3 so chu\u1EA9n WHO\n }\n </div>\n }\n </article>\n\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.heightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-arrows-v\" aria-hidden=\"true\"></i>\n Chi\u1EC1u cao\n </span>\n @if (s.body.heightWho; as hw) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"hw.status\">\n {{ hw.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">\n @if (s.body.heightCm) {\n {{ s.body.heightCm }}<span>cm</span>\n } @else {\n \u2014<span>cm</span>\n }\n </div>\n\n @if (s.body.heightWho; as hw) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ hw.actual }}cm</span>\n <span>Trung v\u1ECB WHO {{ hw.median }}cm</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"hw.status\"\n [style.left.%]=\"whoMarkerPosition(hw.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ hw.percentile }} \u00B7 z {{ formatWhoZ(hw.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"hw.status\">\n {{ hw.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n {{ s.body.heightCm ? 'Thi\u1EBFu ng\u00E0y sinh/gi\u1EDBi t\u00EDnh \u0111\u1EC3 so chu\u1EA9n' : 'Ch\u01B0a \u0111o chi\u1EC1u cao' }}\n </div>\n }\n </article>\n </div>\n\n <div class=\"ds-body-card__meta\">\n @if (s.body.daysFromViewedDay === 0) {\n C\u00E2n \u0111\u00FAng ng\u00E0y n\u00E0y\n } @else if (s.body.measuredAfter) {\n C\u00E2n ng\u00E0y {{ s.body.measuredDisplay }}, sau ng\u00E0y n\u00E0y {{ s.body.daysFromViewedDay }} ng\u00E0y\n } @else {\n C\u00E2n g\u1EA7n nh\u1EA5t tr\u01B0\u1EDBc \u0111\u00F3, ng\u00E0y {{ s.body.measuredDisplay }}\n }\n @if (s.body.heightDate && s.body.heightDate !== s.body.measuredDate) {\n \u00B7 \u0111o chi\u1EC1u cao ng\u00E0y {{ s.body.heightDisplay }}\n }\n </div>\n\n @if (s.nutrition) {\n <div class=\"ds-target\">\n <div class=\"ds-target__head\">\n <span class=\"ds-target__label\">L\u01B0\u1EE3ng s\u1EEFa so khuy\u1EBFn ngh\u1ECB</span>\n <span\n class=\"ds-body-card__percent\"\n [class.ds-body-card__percent--low]=\"s.nutrition.percentOfMin < 85\"\n [class.ds-body-card__percent--ok]=\"s.nutrition.percentOfMin >= 85\"\n >\n {{ s.nutrition.percentOfMin }}%\n </span>\n </div>\n <div class=\"ds-target__track\">\n <span\n class=\"ds-target__fill\"\n [class.ds-target__fill--low]=\"s.nutrition.percentOfMin < 85\"\n [style.width.%]=\"s.nutrition.barPercent\"\n ></span>\n </div>\n <div class=\"ds-target__scale\">\n <span>0 ml</span>\n <span>\n Khuy\u1EBFn ngh\u1ECB {{ s.nutrition.dailyMlMin }}\u2013{{ s.nutrition.dailyMlMax }} ml m\u1ED7i ng\u00E0y\n </span>\n </div>\n <div class=\"ds-target__note\">\n @if (s.nutrition.remainingToMin > 0) {\n B\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml, c\u00F2n thi\u1EBFu {{ s.nutrition.remainingToMin }}ml n\u1EEFa m\u1EDBi \u0111\u1EA1t\n m\u1EE9c t\u1ED1i thi\u1EC3u.\n } @else {\n {{ s.nutrition.statusLabel }} \u2014 b\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml trong ng\u00E0y.\n }\n </div>\n </div>\n } @else {\n <div class=\"ds-body-card__meta\">\n Ch\u01B0a t\u00EDnh \u0111\u01B0\u1EE3c khuy\u1EBFn ngh\u1ECB l\u01B0\u1EE3ng s\u1EEFa v\u00EC thi\u1EBFu ng\u00E0y sinh c\u1EE7a b\u00E9.\n </div>\n }\n </section>\n }\n\n <!-- Nh\u00ECn l\u1EA1i -->\n @if (s.recent.length > 0) {\n <section aria-label=\"Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc\">\n <div class=\"ds-head ds-head--pink\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"ds-head__count\">{{ s.recent.length }}</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Y t\u1EBF {{ lookBackDays }} ng\u00E0y g\u1EA7n \u0111\u00E2y</span>\n </div>\n <div class=\"ds-list\">\n @for (item of s.recent; track item.date + item.title) {\n <button\n type=\"button\"\n class=\"ds-related\"\n [style.border-left-color]=\"item.color\"\n (click)=\"openDate(item.date)\"\n >\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y tr\u01B0\u1EDBc</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n <i class=\"pi pi-chevron-right ds-related__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA b\u1ECB d\u00E3n -->\n @if (s.stretchedGaps.length > 0) {\n <section aria-label=\"C\u1EEF b\u00FA b\u1ECB d\u00E3n\">\n <div class=\"ds-head ds-head--orange\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA b\u1ECB d\u00E3n</span>\n <span class=\"ds-head__count\">{{ s.stretchedGaps.length }} l\u1EA7n</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Ng\u01B0\u1EE1ng {{ s.stretchThresholdHuman }}</span>\n </div>\n <div class=\"ds-list\">\n @for (gap of s.stretchedGaps; track gap.from + gap.to) {\n <div class=\"ds-gap\">\n <div class=\"ds-gap__row\">\n <span class=\"ds-gap__time\">{{ gap.from }}</span>\n <span class=\"ds-gap__track\">\n <span class=\"ds-gap__fill\" [style.width.%]=\"gap.barPercent\"></span>\n <span class=\"ds-gap__value\">{{ gap.humanGap }}</span>\n </span>\n <span class=\"ds-gap__time\">{{ gap.to }}</span>\n </div>\n <div class=\"ds-gap__foot\">\n <span class=\"ds-gap__note\">\n {{ gap.note || 'Kho\u1EA3ng c\u00E1ch d\u00E0i h\u01A1n nh\u1ECBp b\u00FA th\u01B0\u1EDDng ng\u00E0y' }}\n </span>\n <span class=\"ds-gap__volume\">{{ gap.volume }}ml</span>\n </div>\n </div>\n }\n <div class=\"ds-hint\">\n Kho\u1EA3ng c\u00E1ch trung b\u00ECnh h\u00F4m nay {{ s.avgGapHuman }}, d\u00E0i nh\u1EA5t {{ s.maxGapHuman }}\n </div>\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA -->\n <section aria-label=\"C\u1EEF b\u00FA\">\n <div class=\"ds-head ds-head--cyan\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA</span>\n @if (s.feedCount > 0) {\n <span class=\"ds-head__count\">{{ s.feedCount }} c\u1EEF b\u00FA</span>\n }\n </div>\n @if (!s.hasFeeding) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 c\u1EEF b\u00FA n\u00E0o \u0111\u01B0\u1EE3c ghi nh\u1EADn trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-feed-periods\">\n @for (period of s.feedPeriods; track period.id) {\n <div class=\"ds-period\" [attr.aria-label]=\"period.label\">\n <div class=\"ds-period__head\">\n <span class=\"ds-period__label\">{{ period.label }}</span>\n <span class=\"ds-period__meta\">\n {{ period.feedCount }} c\u1EEF \u00B7 {{ period.totalMl }}ml\n </span>\n </div>\n <div class=\"ds-feeds\">\n @for (feed of period.feeds; track feed.time + feed.volume + $index) {\n <div class=\"ds-feed-block\">\n <div class=\"ds-feed\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"feed.night\"\n [class.ds-feed__time--low]=\"feed.level === 'under' || feed.level === 'low'\"\n [class.ds-feed__time--high]=\"feed.level === 'high'\"\n >\n {{ feed.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"feed.night && feed.level === 'normal'\"\n [class.ds-feed__fill--under]=\"feed.level === 'under'\"\n [class.ds-feed__fill--low]=\"feed.level === 'low'\"\n [class.ds-feed__fill--high]=\"feed.level === 'high'\"\n [style.width.%]=\"feed.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ feed.volume }}ml</span>\n <span class=\"ds-feed__flag\">\n @if (feed.level === 'under' || feed.level === 'low') {\n <i\n class=\"pi pi-arrow-down ds-feed__flag--low\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n } @else if (feed.level === 'high') {\n <i\n class=\"pi pi-arrow-up ds-feed__flag--high\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n }\n </span>\n </div>\n @if (feed.memberCount > 1) {\n <div class=\"ds-feed__children\">\n @for (m of feed.members; track m.time + m.volume) {\n <div class=\"ds-feed ds-feed--child\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"m.night\"\n >\n {{ m.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"m.night\"\n [style.width.%]=\"m.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ m.volume }}ml</span>\n <span class=\"ds-feed__flag\" aria-hidden=\"true\"></span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n @if (lowFeedCount() > 0) {\n <div class=\"ds-hint ds-hint--amber\">\n {{ lowFeedCount() }} c\u1EEF \u00EDt h\u01A1n m\u1EE9c g\u1EE3i \u00FD\n @if (s.nutrition) {\n kho\u1EA3ng {{ s.nutrition.expectedPerFeed }}ml m\u1ED7i c\u1EEF v\u1EDBi {{ s.feedCount }} c\u1EEF trong ng\u00E0y\n } @else {\n so v\u1EDBi nh\u1ECBp b\u00FA chung c\u1EE7a ng\u00E0y\n }\n </div>\n }\n }\n </section>\n\n <!-- Ho\u1EA1t \u0111\u1ED9ng -->\n <section aria-label=\"Ho\u1EA1t \u0111\u1ED9ng\">\n <div class=\"ds-head ds-head--purple\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Ho\u1EA1t \u0111\u1ED9ng</span>\n @if (s.activities.length > 0) {\n <span class=\"ds-head__count\">{{ s.activities.length }}</span>\n }\n </div>\n @if (loadingMedical()) {\n <div class=\"ds-empty\">\u0110ang t\u1EA3i ho\u1EA1t \u0111\u1ED9ng y t\u1EBF\u2026</div>\n } @else if (s.activities.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 ho\u1EA1t \u0111\u1ED9ng n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (act of s.activities; track act.title) {\n <div class=\"ds-card\" [style.border-left-color]=\"act.kindColor\">\n <div class=\"ds-card__top\">\n <span\n class=\"ds-tag\"\n [style.color]=\"act.kindColor\"\n [style.border-color]=\"act.kindColor\"\n >\n <i class=\"fa-solid\" [ngClass]=\"act.kindIcon\" aria-hidden=\"true\"></i>\n {{ act.kindLabel }}\n </span>\n @if (act.place) {\n <span class=\"ds-card__place\">{{ act.place }}</span>\n }\n </div>\n <div class=\"ds-card__title\">{{ act.title }}</div>\n @if (act.detail) {\n <div class=\"ds-card__detail\">{{ act.detail }}</div>\n }\n </div>\n }\n </div>\n }\n </section>\n\n <!-- L\u1ECBch -->\n <section aria-label=\"L\u1ECBch\">\n <div class=\"ds-head ds-head--blue\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">L\u1ECBch</span>\n @if (s.events.length > 0) {\n <span class=\"ds-head__count\">{{ s.events.length }}</span>\n }\n </div>\n @if (s.events.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (ev of s.events; track ev.time + ev.title) {\n <div class=\"ds-event\">\n <span class=\"ds-event__time\">{{ ev.time }}</span>\n <span class=\"ds-event__body\">\n <span class=\"ds-event__title\">{{ ev.title }}</span>\n @if (ev.place) {\n <span class=\"ds-event__meta\">{{ ev.place }}</span>\n }\n @if (ev.note) {\n <span class=\"ds-event__meta\">{{ ev.note }}</span>\n }\n </span>\n </div>\n }\n </div>\n }\n </section>\n\n <!-- S\u1EAFp t\u1EDBi -->\n <section aria-label=\"S\u1EAFp t\u1EDBi\">\n <div class=\"ds-head ds-head--green\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">S\u1EAFp t\u1EDBi</span>\n @if (s.upcoming.length > 0) {\n <span class=\"ds-head__count\">{{ s.upcoming.length }}</span>\n }\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">{{ lookAheadDays }} ng\u00E0y t\u1EDBi</span>\n </div>\n @if (s.upcoming.length === 0) {\n <div class=\"ds-empty\">Ch\u01B0a c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong {{ lookAheadDays }} ng\u00E0y t\u1EDBi.</div>\n } @else {\n <div class=\"ds-list\">\n @for (item of s.upcoming; track item.date + item.title) {\n <div class=\"ds-related ds-related--static\" [style.border-left-color]=\"item.color\">\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y n\u1EEFa</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n </div>\n }\n </div>\n }\n </section>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f4f6fb;--surface: #ffffff;--border: #dfe5ef;--text: #0b1220;--muted: #5a6a85;--faint: #8c9ab1;--cyan: #0891b2;--cyan-soft: #dff7fd;--cyan-line: #7fdcee;--indigo: #5b5bf0;--indigo-soft: #e9e9ff;--indigo-line: #b4b4fb;--orange: #ef7314;--orange-soft: #fff0e2;--orange-line: #fbc088;--amber: #b45309;--amber-strong: #f59e0b;--amber-soft: #fef6e0;--amber-line: #f6cf7d;--purple: #8b3ff5;--blue: #2563eb;--green: #16a34a;--pink: #e0338c;--r-card: 10px;--r-small: 6px;display:block;background:var(--bg);color:var(--text)}.ds{display:flex;flex-direction:column;gap:18px;padding:14px 14px 32px}.ds-datebar{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:8px 10px}.ds-datebar--weekend{background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-datebar--weekend .ds-datebar__nav{color:var(--indigo)}.ds-datebar--weekend .ds-datebar__nav:disabled{color:var(--faint)}.ds-datebar--weekend .ds-datebar__label{color:var(--indigo)}.ds-datebar__nav{border:none;background:transparent;color:var(--cyan);font-size:15px;padding:6px 10px;cursor:pointer}.ds-datebar__nav:disabled{color:var(--faint);cursor:default}.ds-datebar__center{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:4px 8px;cursor:pointer;border-radius:var(--r-small)}.ds-datebar__label{font-size:15px;font-weight:800;color:var(--cyan);text-align:center;line-height:1.25}.ds-datebar__badge{font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase;color:var(--indigo);background:var(--surface);border:1px solid var(--indigo-line);border-radius:999px;padding:1px 7px}.ds-datebar__input{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:none;cursor:pointer}.ds-reset{align-self:center;margin-top:-12px;display:inline-flex;align-items:center;gap:6px;border:1px solid var(--cyan-line);background:var(--surface);color:var(--cyan);border-radius:999px;padding:6px 14px;font-family:inherit;font-size:12px;font-weight:700;cursor:pointer}.ds-reset i{font-size:12px}.ds-datebar--weekend~.ds-reset{border-color:var(--indigo-line);color:var(--indigo)}.ds-hero{background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:14px;color:var(--text)}.ds-hero__top{display:flex;align-items:flex-end;justify-content:space-between;gap:12px}.ds-hero__label{font-size:11px;font-weight:800;color:var(--cyan)}.ds-hero__total{font-size:38px;font-weight:800;line-height:1.05;margin-top:2px;color:var(--cyan)}.ds-hero__unit{font-size:15px;font-weight:700;margin-left:4px}.ds-hero__delta{display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px;margin-top:6px;font-size:12px;font-weight:700;color:var(--muted)}.ds-hero__delta i{font-size:11px}.ds-hero__delta--up{color:#047857}.ds-hero__delta--down{color:#b45309}.ds-hero__delta--flat{color:var(--muted)}.ds-hero__delta-pct{font-weight:800}.ds-hero__right{text-align:right}.ds-hero__count{font-size:18px;font-weight:800}.ds-hero__avg{font-size:11px;font-weight:600;color:var(--muted)}.ds-hero__stats{display:flex;gap:8px;margin-top:14px}.ds-hero__stat{flex:1;background:var(--surface);border:1px solid var(--cyan-line);border-radius:var(--r-small);padding:8px 10px}.ds-hero__stat-label{display:block;font-size:10px;font-weight:700;color:var(--faint)}.ds-hero__stat-value{display:block;font-size:14px;font-weight:800;margin-top:2px;color:var(--text)}.ds-hero__stat-value--green{color:var(--green)}.ds-hero__stat-value--blue{color:var(--blue)}.ds-hero__stat-value--indigo{color:var(--indigo)}.ds-body-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--green);border-radius:var(--r-card);padding:12px 14px}.ds-body-card__title{font-size:12px;font-weight:800;letter-spacing:.02em;color:var(--muted);margin-bottom:10px}.ds-growth{display:grid;grid-template-columns:1fr 1fr;gap:10px}@media(max-width:420px){.ds-growth{grid-template-columns:1fr}}.ds-growth__card{background:var(--bg);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 11px 11px;min-width:0}.ds-growth__card[data-status=normal]{border-color:#86efac;background:#f0fdf4}.ds-growth__card[data-status=low],.ds-growth__card[data-status=very_low]{border-color:var(--amber-line);background:var(--amber-soft)}.ds-growth__card[data-status=high],.ds-growth__card[data-status=very_high]{border-color:#93c5fd;background:#eff6ff}.ds-growth__head{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-bottom:4px}.ds-growth__label{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:800;color:var(--muted)}.ds-growth__label i{font-size:11px;color:var(--green)}.ds-growth__badge{font-size:10px;font-weight:800;border-radius:999px;padding:2px 7px;white-space:nowrap;background:var(--surface);border:1px solid var(--border);color:var(--muted)}.ds-growth__badge[data-status=normal]{color:var(--green);border-color:#86efac}.ds-growth__badge[data-status=low],.ds-growth__badge[data-status=very_low]{color:var(--amber);border-color:var(--amber-line)}.ds-growth__badge[data-status=high],.ds-growth__badge[data-status=very_high]{color:var(--blue);border-color:#93c5fd}.ds-growth__value{font-size:24px;font-weight:800;line-height:1.1;font-variant-numeric:tabular-nums}.ds-growth__value span{font-size:12px;font-weight:700;margin-left:3px;color:var(--muted)}.ds-growth__compare{display:flex;flex-direction:column;gap:1px;margin-top:6px;font-size:10px;font-weight:600;color:var(--faint)}.ds-growth__scale{margin-top:8px}.ds-growth__scale-labels{display:flex;justify-content:space-between;font-size:9px;font-weight:700;color:var(--faint);margin-bottom:3px}.ds-growth__scale-mid{color:var(--green)}.ds-growth__scale-track{position:relative;display:flex;height:10px;border-radius:5px;overflow:hidden;background:var(--border)}.ds-growth__band{display:block;height:100%}.ds-growth__band--low{width:25%;background:#fcd34d}.ds-growth__band--mid{width:50%;background:#86efac}.ds-growth__band--high{width:25%;background:#93c5fd}.ds-growth__marker{position:absolute;top:50%;width:12px;height:12px;margin-left:-6px;margin-top:-6px;border-radius:50%;background:var(--text);border:2px solid #ffffff;box-sizing:border-box}.ds-growth__marker[data-status=normal]{background:var(--green)}.ds-growth__marker[data-status=low],.ds-growth__marker[data-status=very_low]{background:var(--amber)}.ds-growth__marker[data-status=high],.ds-growth__marker[data-status=very_high]{background:var(--blue)}.ds-growth__scale-legend{display:flex;justify-content:space-between;margin-top:3px;font-size:9px;font-weight:700;color:var(--faint);font-variant-numeric:tabular-nums}.ds-growth__delta{margin-top:7px;font-size:11px;font-weight:700;color:var(--green)}.ds-growth__delta[data-status=low],.ds-growth__delta[data-status=very_low]{color:var(--amber)}.ds-growth__delta[data-status=high],.ds-growth__delta[data-status=very_high]{color:var(--blue)}.ds-growth__delta[data-status=unknown]{color:var(--muted)}.ds-growth__empty{margin-top:8px;font-size:11px;font-weight:600;color:var(--faint);line-height:1.4}.ds-body-card__percent{font-size:18px;font-weight:800;color:var(--green)}.ds-body-card__percent--low{color:var(--amber)}.ds-body-card__meta{font-size:11px;color:var(--faint);margin-top:8px}.ds-target{margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}.ds-target__head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.ds-target__label{font-size:11px;font-weight:800;color:var(--muted)}.ds-target__track{height:10px;background:var(--bg);border-radius:3px;overflow:hidden}.ds-target__fill{display:block;height:100%;background:var(--green)}.ds-target__fill--low{background:var(--amber-strong)}.ds-target__scale{display:flex;justify-content:space-between;gap:10px;font-size:10px;color:var(--faint);margin-top:4px}.ds-target__note{font-size:12px;color:var(--muted);margin-top:6px}.ds-night{background:var(--indigo-soft);border:1px solid var(--indigo-line);border-left:4px solid var(--indigo);border-radius:var(--r-card);padding:10px 12px;font-size:12px;font-weight:700;color:var(--indigo)}.ds-strip{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 6px 8px}.ds-strip__frame{display:flex;align-items:stretch;gap:2px}.ds-strip__nav{flex-shrink:0;align-self:center;width:26px;height:52px;border:1px solid var(--border);border-radius:8px;background:#f8fafc;color:var(--cyan);font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0}.ds-strip__nav:disabled{color:var(--faint);cursor:default;opacity:.45}.ds-strip__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.ds-strip__chart{display:flex;gap:1px;align-items:flex-end;height:56px;padding:0 0 2px}.ds-strip__bar-col{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:2px;height:100%}.ds-strip__bar-track{flex:1;width:100%;max-width:18px;display:flex;align-items:flex-end;justify-content:center}.ds-strip__bar{width:100%;min-height:3px;border-radius:4px 4px 2px 2px;background:linear-gradient(180deg,#22d3ee,var(--cyan));transition:height .2s ease}.ds-strip__bar--empty{min-height:2px;height:2px!important;background:#e2e8f0;opacity:.7}.ds-strip__bar-col--current .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#34d399,#059669)}.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#a5b4fc,var(--indigo))}.ds-strip__bar-col--current.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#818cf8,#4f46e5)}.ds-strip__bar-label{font-size:8px;font-weight:800;color:var(--muted);line-height:1;font-variant-numeric:tabular-nums}.ds-strip__bar-col--current .ds-strip__bar-label{color:var(--cyan)}.ds-strip__row{display:flex;gap:1px}.ds-strip__cell{flex:1;min-width:0;border:none;background:transparent;padding:2px 0 0;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px;font-family:inherit}.ds-strip__cell:disabled{cursor:default;opacity:.4}.ds-strip__dow{font-size:9px;font-weight:700;color:var(--faint)}.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__day{width:22px;height:22px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:var(--faint);border:1px solid transparent}.ds-strip__day--data{color:var(--text);border-color:var(--border)}.ds-strip__cell--current .ds-strip__dow{color:var(--cyan)}.ds-strip__cell--current .ds-strip__day{background:var(--cyan);border-color:var(--cyan);color:#fff}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__day{background:var(--indigo);border-color:var(--indigo)}.ds-strip__dot{width:4px;height:4px;border-radius:2px;background:transparent;display:inline-block}.ds-strip__dot--activity{background:var(--purple)}.ds-strip__dot--event{background:var(--blue)}.ds-strip__legend{display:flex;gap:12px;margin-top:6px;padding-left:2px;font-size:10px;color:var(--muted)}.ds-strip__legend span{display:inline-flex;align-items:center;gap:5px}.ds-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;--tone: var(--cyan)}.ds-head--cyan{--tone: var(--cyan)}.ds-head--orange{--tone: var(--orange)}.ds-head--purple{--tone: var(--purple)}.ds-head--blue{--tone: var(--blue)}.ds-head--green{--tone: var(--green)}.ds-head--pink{--tone: var(--pink)}.ds-head__bar{width:4px;height:16px;border-radius:1px;background:var(--tone)}.ds-head__label{font-size:14px;font-weight:700}.ds-head__count{font-size:11px;font-weight:700;color:#fff;background:var(--tone);border-radius:var(--r-small);padding:2px 7px}.ds-head__spacer{flex:1}.ds-head__action{font-size:11px;font-weight:700;color:var(--tone)}.ds-list{display:flex;flex-direction:column;gap:8px}.ds-empty{background:var(--surface);border:1px dashed var(--border);border-radius:var(--r-card);padding:12px 14px;font-size:12px;color:var(--faint)}.ds-hint{font-size:11px;color:var(--faint)}.ds-hint--amber{color:var(--amber);font-weight:700;margin-top:8px}.ds-tag{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:700;border:1px solid var(--border);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-tag--indigo{color:var(--indigo);background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-tag--amber{color:var(--amber);background:var(--amber-soft);border-color:var(--amber-line)}.ds-tag--cyan{color:var(--cyan);background:var(--cyan-soft);border-color:var(--cyan-line)}.ds-gap{background:var(--surface);border:1px solid var(--orange-line);border-left:4px solid var(--orange);border-radius:var(--r-card);padding:11px 14px}.ds-gap__row{display:flex;align-items:center;gap:10px}.ds-gap__time{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums}.ds-gap__track{position:relative;flex:1;height:20px;display:block}.ds-gap__track:before{content:\"\";position:absolute;top:9px;left:0;right:0;height:3px;background:var(--orange-soft)}.ds-gap__fill{position:absolute;top:9px;left:0;height:3px;background:var(--orange)}.ds-gap__value{position:absolute;top:-1px;left:50%;transform:translate(-50%);font-size:11px;font-weight:800;color:#fff;background:var(--orange);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-gap__foot{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:8px}.ds-gap__note{font-size:12px;color:var(--muted)}.ds-gap__volume{font-size:12px;font-weight:700;white-space:nowrap}.ds-feed-periods{display:flex;flex-direction:column;gap:14px}.ds-period__head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:6px;padding:0 2px}.ds-period__label{font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--cyan)}.ds-period__meta{font-size:11px;font-weight:600;color:var(--faint)}.ds-feeds{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:12px;display:flex;flex-direction:column;gap:10px}.ds-feed-block{display:flex;flex-direction:column;gap:5px}.ds-feed{display:grid;grid-template-columns:42px minmax(0,1fr) 52px 22px;align-items:center;column-gap:10px}.ds-feed--child{grid-template-columns:42px minmax(0,1fr) 52px 22px;opacity:.92}.ds-feed__children{display:flex;flex-direction:column;gap:5px;padding-left:10px;border-left:2px solid var(--cyan-line);margin-left:6px}.ds-feed__time{width:42px;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums}.ds-feed__time--night{color:var(--indigo)}.ds-feed__time--low{color:var(--amber)}.ds-feed__time--high{color:var(--green)}.ds-feed--child .ds-feed__time{font-weight:600;color:var(--muted)}.ds-feed--child .ds-feed__time.ds-feed__time--night{color:var(--indigo)}.ds-feed__track{height:12px;background:var(--bg);border-radius:3px;overflow:hidden;display:block;min-width:0}.ds-feed--child .ds-feed__track{height:8px}.ds-feed__fill{display:block;height:100%;background:var(--cyan)}.ds-feed__fill--night{background:var(--indigo)}.ds-feed__fill--under{background:var(--amber-strong)}.ds-feed__fill--low{background:var(--amber-strong);opacity:.75}.ds-feed__fill--high{background:var(--green)}.ds-feed__volume{width:52px;text-align:right;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}.ds-feed--child .ds-feed__volume{font-weight:600;color:var(--muted)}.ds-feed__flag{width:22px;height:18px;display:inline-flex;align-items:center;justify-content:center}.ds-feed__flag .pi{font-size:12px;font-weight:700}.ds-feed__flag--low{color:var(--amber)}.ds-feed__flag--high{color:var(--green)}.ds-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--purple);border-radius:var(--r-card);padding:11px 13px}.ds-card__top{display:flex;align-items:center;gap:8px;margin-bottom:6px}.ds-card__place{font-size:11px;color:var(--faint)}.ds-card__title{font-size:14px;font-weight:700}.ds-card__detail{font-size:12px;color:var(--muted);margin-top:3px}.ds-event{display:flex;gap:12px;align-items:center;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--blue);border-radius:var(--r-card);padding:11px 13px}.ds-event__time{background:var(--blue);color:#fff;border-radius:var(--r-small);padding:6px 9px;font-size:13px;font-weight:800;white-space:nowrap}.ds-event__title{display:block;font-size:14px;font-weight:700}.ds-event__meta{display:block;font-size:12px;color:var(--muted);margin-top:2px}.ds-related{display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--pink);border-radius:var(--r-card);padding:10px 12px;cursor:pointer;color:var(--text);font-family:inherit}.ds-related--static{cursor:default}.ds-related__badge{min-width:62px;text-align:center;border:1px solid currentcolor;border-radius:var(--r-small);padding:5px 4px}.ds-related__days{display:block;font-size:15px;font-weight:800}.ds-related__unit{display:block;font-size:10px;font-weight:700}.ds-related__body{flex:1;min-width:0}.ds-related__title{display:block;font-size:13px;font-weight:700}.ds-related__meta{display:block;font-size:11px;color:var(--muted);margin-top:2px}.ds-related__chev{font-size:12px;color:var(--faint)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21311
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DailySummaryComponent, isStandalone: true, selector: "app-daily-summary", inputs: { logs: { classPropertyName: "logs", publicName: "logs", isSignal: true, isRequired: false, transformFunction: null }, birthDate: { classPropertyName: "birthDate", publicName: "birthDate", isSignal: true, isRequired: false, transformFunction: null }, initialDate: { classPropertyName: "initialDate", publicName: "initialDate", isSignal: true, isRequired: false, transformFunction: null }, feedGroupGapMinutes: { classPropertyName: "feedGroupGapMinutes", publicName: "feedGroupGapMinutes", isSignal: true, isRequired: false, transformFunction: null }, gender: { classPropertyName: "gender", publicName: "gender", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@let s = summary();\n\n<div class=\"ds\">\n <!-- \u0110i\u1EC1u h\u01B0\u1EDBng ng\u00E0y -->\n <div class=\"ds-datebar\" [class.ds-datebar--weekend]=\"s.weekend\">\n <button type=\"button\" class=\"ds-datebar__nav\" (click)=\"shiftDay(-1)\" aria-label=\"Ng\u00E0y tr\u01B0\u1EDBc\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <label class=\"ds-datebar__center\">\n <span class=\"ds-datebar__label\">\n {{ s.weekday }}, {{ s.displayDate }}\n </span>\n @if (s.weekend) {\n <span class=\"ds-datebar__badge\">Cu\u1ED1i tu\u1EA7n</span>\n }\n <input\n type=\"date\"\n class=\"ds-datebar__input\"\n [value]=\"s.date\"\n [max]=\"maxDate\"\n (change)=\"onDateInput($any($event.target).value)\"\n aria-label=\"Ch\u1ECDn ng\u00E0y\"\n />\n </label>\n\n <button\n type=\"button\"\n class=\"ds-datebar__nav\"\n [disabled]=\"!canGoForward()\"\n (click)=\"shiftDay(1)\"\n aria-label=\"Ng\u00E0y sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @if (canGoForward()) {\n <button type=\"button\" class=\"ds-reset\" (click)=\"goLatest()\">\n <i class=\"pi pi-replay\" aria-hidden=\"true\"></i>\n V\u1EC1 ng\u00E0y m\u1EDBi nh\u1EA5t ({{ maxDateLabel }})\n </button>\n }\n\n <!-- T\u1ED5ng quan -->\n <section class=\"ds-hero\" aria-label=\"T\u1ED5ng quan trong ng\u00E0y\">\n <div class=\"ds-hero__top\">\n <div>\n <div class=\"ds-hero__label\">T\u1ED4NG L\u01AF\u1EE2NG S\u1EEEA TRONG NG\u00C0Y</div>\n <div class=\"ds-hero__total\">{{ s.totalMl }}<span class=\"ds-hero__unit\">ml</span></div>\n @if (s.hasFeeding || s.yesterday.totalMl > 0) {\n <div\n class=\"ds-hero__delta\"\n [class.ds-hero__delta--up]=\"s.yesterday.deltaMl > 0\"\n [class.ds-hero__delta--down]=\"s.yesterday.deltaMl < 0\"\n [class.ds-hero__delta--flat]=\"s.yesterday.deltaMl === 0\"\n >\n @if (s.yesterday.totalMl === 0 && s.hasFeeding) {\n H\u00F4m qua ch\u01B0a ghi nh\u1EADn c\u1EEF\n } @else if (s.yesterday.deltaMl === 0) {\n B\u1EB1ng h\u00F4m qua ({{ s.yesterday.totalMl }}ml)\n } @else {\n <i\n class=\"pi\"\n [class.pi-arrow-up]=\"s.yesterday.deltaMl > 0\"\n [class.pi-arrow-down]=\"s.yesterday.deltaMl < 0\"\n aria-hidden=\"true\"\n ></i>\n {{ s.yesterday.deltaMl > 0 ? '+' : '' }}{{ s.yesterday.deltaMl }}ml\n @if (s.yesterday.deltaPercent !== null) {\n <span class=\"ds-hero__delta-pct\">\n ({{ s.yesterday.deltaPercent > 0 ? '+' : '' }}{{ s.yesterday.deltaPercent }}%)\n </span>\n }\n so v\u1EDBi h\u00F4m qua\n }\n </div>\n }\n </div>\n <div class=\"ds-hero__right\">\n <div class=\"ds-hero__count\">{{ s.feedCount }} c\u1EEF b\u00FA</div>\n <div class=\"ds-hero__avg\">\n @if (s.feedCount > 0) {\n trung b\u00ECnh {{ s.avgMl }}ml m\u1ED7i c\u1EEF\n } @else {\n ch\u01B0a ghi nh\u1EADn c\u1EEF n\u00E0o\n }\n </div>\n </div>\n </div>\n\n <div class=\"ds-hero__stats\">\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF cao nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--green\">\n {{ s.hasFeeding ? s.maxMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF th\u1EA5p nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--blue\">\n {{ s.hasFeeding ? s.minMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">B\u00FA \u0111\u00EAm</span>\n <span\n class=\"ds-hero__stat-value\"\n [class.ds-hero__stat-value--indigo]=\"s.nightCount > 0\"\n [class.ds-hero__stat-value--green]=\"s.nightCount === 0\"\n >\n {{ s.nightCount > 0 ? s.nightCount + ' c\u1EEF' : 'Kh\u00F4ng c\u00F3' }}\n </span>\n </div>\n </div>\n </section>\n\n @if (s.nightCount > 0) {\n <div class=\"ds-night\">\n B\u00E9 c\u00F3 b\u00FA \u0111\u00EAm trong khung 00:00 \u2013 05:00: {{ s.nightCount }} c\u1EEF b\u00FA, t\u1ED5ng {{ s.nightMl }}ml\n </div>\n }\n\n <!-- D\u1EA3i hai tu\u1EA7n -->\n <section class=\"ds-strip\" aria-label=\"D\u1EA3i hai tu\u1EA7n\">\n <div class=\"ds-strip__frame\">\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n (click)=\"shiftStripWeek(-2)\"\n aria-label=\"Tu\u1EA7n tr\u01B0\u1EDBc\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"ds-strip__content\">\n <div class=\"ds-strip__chart\" aria-hidden=\"true\">\n @for (cell of s.strip; track cell.date) {\n <div\n class=\"ds-strip__bar-col\"\n [class.ds-strip__bar-col--current]=\"cell.current\"\n [class.ds-strip__bar-col--weekend]=\"cell.weekend\"\n >\n <div class=\"ds-strip__bar-track\">\n <span\n class=\"ds-strip__bar\"\n [class.ds-strip__bar--empty]=\"!cell.hasFeeding\"\n [style.height.%]=\"cell.barPercent\"\n ></span>\n </div>\n @if (cell.hasFeeding) {\n <span class=\"ds-strip__bar-label\">{{ cell.totalMl }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"ds-strip__row\">\n @for (cell of s.strip; track cell.date) {\n <button\n type=\"button\"\n class=\"ds-strip__cell\"\n [class.ds-strip__cell--current]=\"cell.current\"\n [class.ds-strip__cell--weekend]=\"cell.weekend\"\n [disabled]=\"!isNavigable(cell.date)\"\n (click)=\"openDate(cell.date)\"\n [attr.aria-current]=\"cell.current ? 'date' : null\"\n [attr.aria-label]=\"cell.weekdayShort + ' ng\u00E0y ' + cell.dayNumber\"\n >\n <span class=\"ds-strip__dow\">{{ cell.weekdayShort }}</span>\n <span class=\"ds-strip__day\" [class.ds-strip__day--data]=\"cell.hasFeeding\">\n {{ cell.dayNumber }}\n </span>\n <span\n class=\"ds-strip__dot\"\n [class.ds-strip__dot--activity]=\"cell.hasActivity\"\n [class.ds-strip__dot--event]=\"!cell.hasActivity && cell.hasEvent\"\n ></span>\n </button>\n }\n </div>\n </div>\n\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n [disabled]=\"!canShiftStripForward()\"\n (click)=\"shiftStripWeek(2)\"\n aria-label=\"Tu\u1EA7n sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"ds-strip__legend\">\n <span><i class=\"ds-strip__dot ds-strip__dot--activity\"></i> C\u00F3 ho\u1EA1t \u0111\u1ED9ng</span>\n <span><i class=\"ds-strip__dot ds-strip__dot--event\"></i> C\u00F3 l\u1ECBch h\u1EB9n</span>\n </div>\n </section>\n\n <!-- Th\u1EC3 tr\u1EA1ng & m\u1EE9c \u0111\u1EA1t khuy\u1EBFn ngh\u1ECB -->\n @if (s.body) {\n <section class=\"ds-body-card\" aria-label=\"Th\u1EC3 tr\u1EA1ng v\u00E0 khuy\u1EBFn ngh\u1ECB\">\n <div class=\"ds-body-card__title\">Th\u1EC3 tr\u1EA1ng &amp; khuy\u1EBFn ngh\u1ECB</div>\n\n <div class=\"ds-growth\">\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.weightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n C\u00E2n n\u1EB7ng\n </span>\n @if (s.body.weightWho; as ww) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"ww.status\">\n {{ ww.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">{{ s.body.weightKg }}<span>kg</span></div>\n\n @if (s.body.weightWho; as ww) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ ww.actual }}kg</span>\n <span>Trung v\u1ECB WHO {{ ww.median }}kg</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"ww.status\"\n [style.left.%]=\"whoMarkerPosition(ww.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ ww.percentile }} \u00B7 z {{ formatWhoZ(ww.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"ww.status\">\n {{ ww.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n @if (s.body.ageInDays !== null) {\n Th\u00EAm gi\u1EDBi t\u00EDnh trong profile \u0111\u1EC3 so chu\u1EA9n WHO\n } @else {\n C\u1EA7n ng\u00E0y sinh \u0111\u1EC3 so chu\u1EA9n WHO\n }\n </div>\n }\n </article>\n\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.heightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-arrows-v\" aria-hidden=\"true\"></i>\n Chi\u1EC1u cao\n </span>\n @if (s.body.heightWho; as hw) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"hw.status\">\n {{ hw.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">\n @if (s.body.heightCm) {\n {{ s.body.heightCm }}<span>cm</span>\n } @else {\n \u2014<span>cm</span>\n }\n </div>\n\n @if (s.body.heightWho; as hw) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ hw.actual }}cm</span>\n <span>Trung v\u1ECB WHO {{ hw.median }}cm</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"hw.status\"\n [style.left.%]=\"whoMarkerPosition(hw.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ hw.percentile }} \u00B7 z {{ formatWhoZ(hw.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"hw.status\">\n {{ hw.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n {{ s.body.heightCm ? 'Thi\u1EBFu ng\u00E0y sinh/gi\u1EDBi t\u00EDnh \u0111\u1EC3 so chu\u1EA9n' : 'Ch\u01B0a \u0111o chi\u1EC1u cao' }}\n </div>\n }\n </article>\n </div>\n\n <div class=\"ds-body-card__meta\">\n @if (s.body.daysFromViewedDay === 0) {\n C\u00E2n \u0111\u00FAng ng\u00E0y n\u00E0y\n } @else if (s.body.measuredAfter) {\n C\u00E2n ng\u00E0y {{ s.body.measuredDisplay }}, sau ng\u00E0y n\u00E0y {{ s.body.daysFromViewedDay }} ng\u00E0y\n } @else {\n C\u00E2n g\u1EA7n nh\u1EA5t tr\u01B0\u1EDBc \u0111\u00F3, ng\u00E0y {{ s.body.measuredDisplay }}\n }\n @if (s.body.heightDate && s.body.heightDate !== s.body.measuredDate) {\n \u00B7 \u0111o chi\u1EC1u cao ng\u00E0y {{ s.body.heightDisplay }}\n }\n </div>\n\n @if (s.nutrition) {\n <div class=\"ds-target\">\n <div class=\"ds-target__head\">\n <span class=\"ds-target__label\">L\u01B0\u1EE3ng s\u1EEFa so khuy\u1EBFn ngh\u1ECB</span>\n <span\n class=\"ds-body-card__percent\"\n [class.ds-body-card__percent--low]=\"s.nutrition.percentOfMin < 85\"\n [class.ds-body-card__percent--ok]=\"s.nutrition.percentOfMin >= 85\"\n >\n {{ s.nutrition.percentOfMin }}%\n </span>\n </div>\n <div class=\"ds-target__track\">\n <span\n class=\"ds-target__fill\"\n [class.ds-target__fill--low]=\"s.nutrition.percentOfMin < 85\"\n [style.width.%]=\"s.nutrition.barPercent\"\n ></span>\n </div>\n <div class=\"ds-target__scale\">\n <span>0 ml</span>\n <span>\n Khuy\u1EBFn ngh\u1ECB {{ s.nutrition.dailyMlMin }}\u2013{{ s.nutrition.dailyMlMax }} ml m\u1ED7i ng\u00E0y\n </span>\n </div>\n <div class=\"ds-target__note\">\n @if (s.nutrition.remainingToMin > 0) {\n B\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml, c\u00F2n thi\u1EBFu {{ s.nutrition.remainingToMin }}ml n\u1EEFa m\u1EDBi \u0111\u1EA1t\n m\u1EE9c t\u1ED1i thi\u1EC3u.\n } @else {\n {{ s.nutrition.statusLabel }} \u2014 b\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml trong ng\u00E0y.\n }\n </div>\n </div>\n } @else {\n <div class=\"ds-body-card__meta\">\n Ch\u01B0a t\u00EDnh \u0111\u01B0\u1EE3c khuy\u1EBFn ngh\u1ECB l\u01B0\u1EE3ng s\u1EEFa v\u00EC thi\u1EBFu ng\u00E0y sinh c\u1EE7a b\u00E9.\n </div>\n }\n </section>\n }\n\n <!-- Nh\u00ECn l\u1EA1i -->\n @if (s.recent.length > 0) {\n <section aria-label=\"Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc\">\n <div class=\"ds-head ds-head--pink\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"ds-head__count\">{{ s.recent.length }}</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Y t\u1EBF {{ lookBackDays }} ng\u00E0y g\u1EA7n \u0111\u00E2y</span>\n </div>\n <div class=\"ds-list\">\n @for (item of s.recent; track item.date + item.title) {\n <button\n type=\"button\"\n class=\"ds-related\"\n [style.border-left-color]=\"item.color\"\n (click)=\"openDate(item.date)\"\n >\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y tr\u01B0\u1EDBc</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n <i class=\"pi pi-chevron-right ds-related__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA b\u1ECB d\u00E3n -->\n @if (s.stretchedGaps.length > 0) {\n <section aria-label=\"C\u1EEF b\u00FA b\u1ECB d\u00E3n\">\n <div class=\"ds-head ds-head--orange\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA b\u1ECB d\u00E3n</span>\n <span class=\"ds-head__count\">{{ s.stretchedGaps.length }} l\u1EA7n</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Ng\u01B0\u1EE1ng {{ s.stretchThresholdHuman }}</span>\n </div>\n <div class=\"ds-list\">\n @for (gap of s.stretchedGaps; track gap.from + gap.to) {\n <div class=\"ds-gap\">\n <div class=\"ds-gap__row\">\n <span class=\"ds-gap__time\">{{ gap.from }}</span>\n <span class=\"ds-gap__track\">\n <span class=\"ds-gap__fill\" [style.width.%]=\"gap.barPercent\"></span>\n <span class=\"ds-gap__value\">{{ gap.humanGap }}</span>\n </span>\n <span class=\"ds-gap__time\">{{ gap.to }}</span>\n </div>\n <div class=\"ds-gap__foot\">\n <span class=\"ds-gap__note\">\n {{ gap.note || 'Kho\u1EA3ng c\u00E1ch d\u00E0i h\u01A1n nh\u1ECBp b\u00FA th\u01B0\u1EDDng ng\u00E0y' }}\n </span>\n <span class=\"ds-gap__volume\">{{ gap.volume }}ml</span>\n </div>\n </div>\n }\n <div class=\"ds-hint\">\n Kho\u1EA3ng c\u00E1ch trung b\u00ECnh h\u00F4m nay {{ s.avgGapHuman }}, d\u00E0i nh\u1EA5t {{ s.maxGapHuman }}\n </div>\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA -->\n <section aria-label=\"C\u1EEF b\u00FA\">\n <div class=\"ds-head ds-head--cyan\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA</span>\n @if (s.feedCount > 0) {\n <span class=\"ds-head__count\">{{ s.feedCount }} c\u1EEF b\u00FA</span>\n }\n </div>\n @if (!s.hasFeeding) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 c\u1EEF b\u00FA n\u00E0o \u0111\u01B0\u1EE3c ghi nh\u1EADn trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-feed-periods\">\n @for (period of s.feedPeriods; track period.id) {\n <div class=\"ds-period\" [attr.aria-label]=\"period.label\">\n <div class=\"ds-period__head\">\n <span class=\"ds-period__label\">{{ period.label }}</span>\n <span class=\"ds-period__meta\">\n {{ period.feedCount }} c\u1EEF \u00B7 {{ period.totalMl }}ml\n </span>\n </div>\n <div class=\"ds-feeds\">\n @for (feed of period.feeds; track feed.time + feed.volume + $index) {\n <div class=\"ds-feed-block\">\n <div class=\"ds-feed\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"feed.night\"\n [class.ds-feed__time--low]=\"feed.level === 'under' || feed.level === 'low'\"\n [class.ds-feed__time--high]=\"feed.level === 'high'\"\n >\n {{ feed.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"feed.night && feed.level === 'normal'\"\n [class.ds-feed__fill--under]=\"feed.level === 'under'\"\n [class.ds-feed__fill--low]=\"feed.level === 'low'\"\n [class.ds-feed__fill--high]=\"feed.level === 'high'\"\n [style.width.%]=\"feed.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ feed.volume }}ml</span>\n <span class=\"ds-feed__flag\">\n @if (feed.level === 'under' || feed.level === 'low') {\n <i\n class=\"pi pi-arrow-down ds-feed__flag--low\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n } @else if (feed.level === 'high') {\n <i\n class=\"pi pi-arrow-up ds-feed__flag--high\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n }\n </span>\n </div>\n @if (feed.memberCount > 1) {\n <div class=\"ds-feed__children\">\n @for (m of feed.members; track m.time + m.volume) {\n <div class=\"ds-feed ds-feed--child\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"m.night\"\n >\n {{ m.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"m.night\"\n [style.width.%]=\"m.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ m.volume }}ml</span>\n <span class=\"ds-feed__flag\" aria-hidden=\"true\"></span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n @if (lowFeedCount() > 0) {\n <div class=\"ds-hint ds-hint--amber\">\n {{ lowFeedCount() }} c\u1EEF \u00EDt h\u01A1n m\u1EE9c g\u1EE3i \u00FD\n @if (s.nutrition) {\n kho\u1EA3ng {{ s.nutrition.expectedPerFeed }}ml m\u1ED7i c\u1EEF v\u1EDBi {{ s.feedCount }} c\u1EEF trong ng\u00E0y\n } @else {\n so v\u1EDBi nh\u1ECBp b\u00FA chung c\u1EE7a ng\u00E0y\n }\n </div>\n }\n }\n </section>\n\n <!-- Ho\u1EA1t \u0111\u1ED9ng -->\n <section aria-label=\"Ho\u1EA1t \u0111\u1ED9ng\">\n <div class=\"ds-head ds-head--purple\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Ho\u1EA1t \u0111\u1ED9ng</span>\n @if (s.activities.length > 0) {\n <span class=\"ds-head__count\">{{ s.activities.length }}</span>\n }\n </div>\n @if (loadingMedical()) {\n <div class=\"ds-empty\">\u0110ang t\u1EA3i ho\u1EA1t \u0111\u1ED9ng y t\u1EBF\u2026</div>\n } @else if (s.activities.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 ho\u1EA1t \u0111\u1ED9ng n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (act of s.activities; track act.title) {\n <div class=\"ds-card\" [style.border-left-color]=\"act.kindColor\">\n <div class=\"ds-card__top\">\n <span\n class=\"ds-tag\"\n [style.color]=\"act.kindColor\"\n [style.border-color]=\"act.kindColor\"\n >\n <i class=\"fa-solid\" [ngClass]=\"act.kindIcon\" aria-hidden=\"true\"></i>\n {{ act.kindLabel }}\n </span>\n @if (act.place) {\n <span class=\"ds-card__place\">{{ act.place }}</span>\n }\n </div>\n <div class=\"ds-card__title\">{{ act.title }}</div>\n @if (act.detail) {\n <div class=\"ds-card__detail\" [innerHTML]=\"act.detail | safeRichHtml\"></div>\n }\n </div>\n }\n </div>\n }\n </section>\n\n <!-- L\u1ECBch -->\n <section aria-label=\"L\u1ECBch\">\n <div class=\"ds-head ds-head--blue\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">L\u1ECBch</span>\n @if (s.events.length > 0) {\n <span class=\"ds-head__count\">{{ s.events.length }}</span>\n }\n </div>\n @if (s.events.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (ev of s.events; track ev.time + ev.title) {\n <div class=\"ds-event\">\n <span class=\"ds-event__time\">{{ ev.time }}</span>\n <span class=\"ds-event__body\">\n <span class=\"ds-event__title\">{{ ev.title }}</span>\n @if (ev.place) {\n <span class=\"ds-event__meta\">{{ ev.place }}</span>\n }\n @if (ev.note) {\n <span class=\"ds-event__meta\">{{ ev.note }}</span>\n }\n </span>\n </div>\n }\n </div>\n }\n </section>\n\n <!-- S\u1EAFp t\u1EDBi -->\n <section aria-label=\"S\u1EAFp t\u1EDBi\">\n <div class=\"ds-head ds-head--green\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">S\u1EAFp t\u1EDBi</span>\n @if (s.upcoming.length > 0) {\n <span class=\"ds-head__count\">{{ s.upcoming.length }}</span>\n }\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">{{ lookAheadDays }} ng\u00E0y t\u1EDBi</span>\n </div>\n @if (s.upcoming.length === 0) {\n <div class=\"ds-empty\">Ch\u01B0a c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong {{ lookAheadDays }} ng\u00E0y t\u1EDBi.</div>\n } @else {\n <div class=\"ds-list\">\n @for (item of s.upcoming; track item.date + item.title) {\n <div class=\"ds-related ds-related--static\" [style.border-left-color]=\"item.color\">\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y n\u1EEFa</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n </div>\n }\n </div>\n }\n </section>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f4f6fb;--surface: #ffffff;--border: #dfe5ef;--text: #0b1220;--muted: #5a6a85;--faint: #8c9ab1;--cyan: #0891b2;--cyan-soft: #dff7fd;--cyan-line: #7fdcee;--indigo: #5b5bf0;--indigo-soft: #e9e9ff;--indigo-line: #b4b4fb;--orange: #ef7314;--orange-soft: #fff0e2;--orange-line: #fbc088;--amber: #b45309;--amber-strong: #f59e0b;--amber-soft: #fef6e0;--amber-line: #f6cf7d;--purple: #8b3ff5;--blue: #2563eb;--green: #16a34a;--pink: #e0338c;--r-card: 10px;--r-small: 6px;display:block;background:var(--bg);color:var(--text)}.ds{display:flex;flex-direction:column;gap:18px;padding:14px 14px 32px}.ds-datebar{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:8px 10px}.ds-datebar--weekend{background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-datebar--weekend .ds-datebar__nav{color:var(--indigo)}.ds-datebar--weekend .ds-datebar__nav:disabled{color:var(--faint)}.ds-datebar--weekend .ds-datebar__label{color:var(--indigo)}.ds-datebar__nav{border:none;background:transparent;color:var(--cyan);font-size:15px;padding:6px 10px;cursor:pointer}.ds-datebar__nav:disabled{color:var(--faint);cursor:default}.ds-datebar__center{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:4px 8px;cursor:pointer;border-radius:var(--r-small)}.ds-datebar__label{font-size:15px;font-weight:800;color:var(--cyan);text-align:center;line-height:1.25}.ds-datebar__badge{font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase;color:var(--indigo);background:var(--surface);border:1px solid var(--indigo-line);border-radius:999px;padding:1px 7px}.ds-datebar__input{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:none;cursor:pointer}.ds-reset{align-self:center;margin-top:-12px;display:inline-flex;align-items:center;gap:6px;border:1px solid var(--cyan-line);background:var(--surface);color:var(--cyan);border-radius:999px;padding:6px 14px;font-family:inherit;font-size:12px;font-weight:700;cursor:pointer}.ds-reset i{font-size:12px}.ds-datebar--weekend~.ds-reset{border-color:var(--indigo-line);color:var(--indigo)}.ds-hero{background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:14px;color:var(--text)}.ds-hero__top{display:flex;align-items:flex-end;justify-content:space-between;gap:12px}.ds-hero__label{font-size:11px;font-weight:800;color:var(--cyan)}.ds-hero__total{font-size:38px;font-weight:800;line-height:1.05;margin-top:2px;color:var(--cyan)}.ds-hero__unit{font-size:15px;font-weight:700;margin-left:4px}.ds-hero__delta{display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px;margin-top:6px;font-size:12px;font-weight:700;color:var(--muted)}.ds-hero__delta i{font-size:11px}.ds-hero__delta--up{color:#047857}.ds-hero__delta--down{color:#b45309}.ds-hero__delta--flat{color:var(--muted)}.ds-hero__delta-pct{font-weight:800}.ds-hero__right{text-align:right}.ds-hero__count{font-size:18px;font-weight:800}.ds-hero__avg{font-size:11px;font-weight:600;color:var(--muted)}.ds-hero__stats{display:flex;gap:8px;margin-top:14px}.ds-hero__stat{flex:1;background:var(--surface);border:1px solid var(--cyan-line);border-radius:var(--r-small);padding:8px 10px}.ds-hero__stat-label{display:block;font-size:10px;font-weight:700;color:var(--faint)}.ds-hero__stat-value{display:block;font-size:14px;font-weight:800;margin-top:2px;color:var(--text)}.ds-hero__stat-value--green{color:var(--green)}.ds-hero__stat-value--blue{color:var(--blue)}.ds-hero__stat-value--indigo{color:var(--indigo)}.ds-body-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--green);border-radius:var(--r-card);padding:12px 14px}.ds-body-card__title{font-size:12px;font-weight:800;letter-spacing:.02em;color:var(--muted);margin-bottom:10px}.ds-growth{display:grid;grid-template-columns:1fr 1fr;gap:10px}@media(max-width:420px){.ds-growth{grid-template-columns:1fr}}.ds-growth__card{background:var(--bg);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 11px 11px;min-width:0}.ds-growth__card[data-status=normal]{border-color:#86efac;background:#f0fdf4}.ds-growth__card[data-status=low],.ds-growth__card[data-status=very_low]{border-color:var(--amber-line);background:var(--amber-soft)}.ds-growth__card[data-status=high],.ds-growth__card[data-status=very_high]{border-color:#93c5fd;background:#eff6ff}.ds-growth__head{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-bottom:4px}.ds-growth__label{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:800;color:var(--muted)}.ds-growth__label i{font-size:11px;color:var(--green)}.ds-growth__badge{font-size:10px;font-weight:800;border-radius:999px;padding:2px 7px;white-space:nowrap;background:var(--surface);border:1px solid var(--border);color:var(--muted)}.ds-growth__badge[data-status=normal]{color:var(--green);border-color:#86efac}.ds-growth__badge[data-status=low],.ds-growth__badge[data-status=very_low]{color:var(--amber);border-color:var(--amber-line)}.ds-growth__badge[data-status=high],.ds-growth__badge[data-status=very_high]{color:var(--blue);border-color:#93c5fd}.ds-growth__value{font-size:24px;font-weight:800;line-height:1.1;font-variant-numeric:tabular-nums}.ds-growth__value span{font-size:12px;font-weight:700;margin-left:3px;color:var(--muted)}.ds-growth__compare{display:flex;flex-direction:column;gap:1px;margin-top:6px;font-size:10px;font-weight:600;color:var(--faint)}.ds-growth__scale{margin-top:8px}.ds-growth__scale-labels{display:flex;justify-content:space-between;font-size:9px;font-weight:700;color:var(--faint);margin-bottom:3px}.ds-growth__scale-mid{color:var(--green)}.ds-growth__scale-track{position:relative;display:flex;height:10px;border-radius:5px;overflow:hidden;background:var(--border)}.ds-growth__band{display:block;height:100%}.ds-growth__band--low{width:25%;background:#fcd34d}.ds-growth__band--mid{width:50%;background:#86efac}.ds-growth__band--high{width:25%;background:#93c5fd}.ds-growth__marker{position:absolute;top:50%;width:12px;height:12px;margin-left:-6px;margin-top:-6px;border-radius:50%;background:var(--text);border:2px solid #ffffff;box-sizing:border-box}.ds-growth__marker[data-status=normal]{background:var(--green)}.ds-growth__marker[data-status=low],.ds-growth__marker[data-status=very_low]{background:var(--amber)}.ds-growth__marker[data-status=high],.ds-growth__marker[data-status=very_high]{background:var(--blue)}.ds-growth__scale-legend{display:flex;justify-content:space-between;margin-top:3px;font-size:9px;font-weight:700;color:var(--faint);font-variant-numeric:tabular-nums}.ds-growth__delta{margin-top:7px;font-size:11px;font-weight:700;color:var(--green)}.ds-growth__delta[data-status=low],.ds-growth__delta[data-status=very_low]{color:var(--amber)}.ds-growth__delta[data-status=high],.ds-growth__delta[data-status=very_high]{color:var(--blue)}.ds-growth__delta[data-status=unknown]{color:var(--muted)}.ds-growth__empty{margin-top:8px;font-size:11px;font-weight:600;color:var(--faint);line-height:1.4}.ds-body-card__percent{font-size:18px;font-weight:800;color:var(--green)}.ds-body-card__percent--low{color:var(--amber)}.ds-body-card__meta{font-size:11px;color:var(--faint);margin-top:8px}.ds-target{margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}.ds-target__head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.ds-target__label{font-size:11px;font-weight:800;color:var(--muted)}.ds-target__track{height:10px;background:var(--bg);border-radius:3px;overflow:hidden}.ds-target__fill{display:block;height:100%;background:var(--green)}.ds-target__fill--low{background:var(--amber-strong)}.ds-target__scale{display:flex;justify-content:space-between;gap:10px;font-size:10px;color:var(--faint);margin-top:4px}.ds-target__note{font-size:12px;color:var(--muted);margin-top:6px}.ds-night{background:var(--indigo-soft);border:1px solid var(--indigo-line);border-left:4px solid var(--indigo);border-radius:var(--r-card);padding:10px 12px;font-size:12px;font-weight:700;color:var(--indigo)}.ds-strip{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 6px 8px}.ds-strip__frame{display:flex;align-items:stretch;gap:2px}.ds-strip__nav{flex-shrink:0;align-self:center;width:26px;height:52px;border:1px solid var(--border);border-radius:8px;background:#f8fafc;color:var(--cyan);font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0}.ds-strip__nav:disabled{color:var(--faint);cursor:default;opacity:.45}.ds-strip__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.ds-strip__chart{display:flex;gap:1px;align-items:flex-end;height:56px;padding:0 0 2px}.ds-strip__bar-col{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:2px;height:100%}.ds-strip__bar-track{flex:1;width:100%;max-width:18px;display:flex;align-items:flex-end;justify-content:center}.ds-strip__bar{width:100%;min-height:3px;border-radius:4px 4px 2px 2px;background:linear-gradient(180deg,#22d3ee,var(--cyan));transition:height .2s ease}.ds-strip__bar--empty{min-height:2px;height:2px!important;background:#e2e8f0;opacity:.7}.ds-strip__bar-col--current .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#34d399,#059669)}.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#a5b4fc,var(--indigo))}.ds-strip__bar-col--current.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#818cf8,#4f46e5)}.ds-strip__bar-label{font-size:8px;font-weight:800;color:var(--muted);line-height:1;font-variant-numeric:tabular-nums}.ds-strip__bar-col--current .ds-strip__bar-label{color:var(--cyan)}.ds-strip__row{display:flex;gap:1px}.ds-strip__cell{flex:1;min-width:0;border:none;background:transparent;padding:2px 0 0;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px;font-family:inherit}.ds-strip__cell:disabled{cursor:default;opacity:.4}.ds-strip__dow{font-size:9px;font-weight:700;color:var(--faint)}.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__day{width:22px;height:22px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:var(--faint);border:1px solid transparent}.ds-strip__day--data{color:var(--text);border-color:var(--border)}.ds-strip__cell--current .ds-strip__dow{color:var(--cyan)}.ds-strip__cell--current .ds-strip__day{background:var(--cyan);border-color:var(--cyan);color:#fff}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__day{background:var(--indigo);border-color:var(--indigo)}.ds-strip__dot{width:4px;height:4px;border-radius:2px;background:transparent;display:inline-block}.ds-strip__dot--activity{background:var(--purple)}.ds-strip__dot--event{background:var(--blue)}.ds-strip__legend{display:flex;gap:12px;margin-top:6px;padding-left:2px;font-size:10px;color:var(--muted)}.ds-strip__legend span{display:inline-flex;align-items:center;gap:5px}.ds-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;--tone: var(--cyan)}.ds-head--cyan{--tone: var(--cyan)}.ds-head--orange{--tone: var(--orange)}.ds-head--purple{--tone: var(--purple)}.ds-head--blue{--tone: var(--blue)}.ds-head--green{--tone: var(--green)}.ds-head--pink{--tone: var(--pink)}.ds-head__bar{width:4px;height:16px;border-radius:1px;background:var(--tone)}.ds-head__label{font-size:14px;font-weight:700}.ds-head__count{font-size:11px;font-weight:700;color:#fff;background:var(--tone);border-radius:var(--r-small);padding:2px 7px}.ds-head__spacer{flex:1}.ds-head__action{font-size:11px;font-weight:700;color:var(--tone)}.ds-list{display:flex;flex-direction:column;gap:8px}.ds-empty{background:var(--surface);border:1px dashed var(--border);border-radius:var(--r-card);padding:12px 14px;font-size:12px;color:var(--faint)}.ds-hint{font-size:11px;color:var(--faint)}.ds-hint--amber{color:var(--amber);font-weight:700;margin-top:8px}.ds-tag{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:700;border:1px solid var(--border);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-tag--indigo{color:var(--indigo);background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-tag--amber{color:var(--amber);background:var(--amber-soft);border-color:var(--amber-line)}.ds-tag--cyan{color:var(--cyan);background:var(--cyan-soft);border-color:var(--cyan-line)}.ds-gap{background:var(--surface);border:1px solid var(--orange-line);border-left:4px solid var(--orange);border-radius:var(--r-card);padding:11px 14px}.ds-gap__row{display:flex;align-items:center;gap:10px}.ds-gap__time{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums}.ds-gap__track{position:relative;flex:1;height:20px;display:block}.ds-gap__track:before{content:\"\";position:absolute;top:9px;left:0;right:0;height:3px;background:var(--orange-soft)}.ds-gap__fill{position:absolute;top:9px;left:0;height:3px;background:var(--orange)}.ds-gap__value{position:absolute;top:-1px;left:50%;transform:translate(-50%);font-size:11px;font-weight:800;color:#fff;background:var(--orange);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-gap__foot{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:8px}.ds-gap__note{font-size:12px;color:var(--muted)}.ds-gap__volume{font-size:12px;font-weight:700;white-space:nowrap}.ds-feed-periods{display:flex;flex-direction:column;gap:14px}.ds-period__head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:6px;padding:0 2px}.ds-period__label{font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--cyan)}.ds-period__meta{font-size:11px;font-weight:600;color:var(--faint)}.ds-feeds{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:12px;display:flex;flex-direction:column;gap:10px}.ds-feed-block{display:flex;flex-direction:column;gap:5px}.ds-feed{display:grid;grid-template-columns:42px minmax(0,1fr) 52px 22px;align-items:center;column-gap:10px}.ds-feed--child{grid-template-columns:42px minmax(0,1fr) 52px 22px;opacity:.92}.ds-feed__children{display:flex;flex-direction:column;gap:5px;padding-left:10px;border-left:2px solid var(--cyan-line);margin-left:6px}.ds-feed__time{width:42px;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums}.ds-feed__time--night{color:var(--indigo)}.ds-feed__time--low{color:var(--amber)}.ds-feed__time--high{color:var(--green)}.ds-feed--child .ds-feed__time{font-weight:600;color:var(--muted)}.ds-feed--child .ds-feed__time.ds-feed__time--night{color:var(--indigo)}.ds-feed__track{height:12px;background:var(--bg);border-radius:3px;overflow:hidden;display:block;min-width:0}.ds-feed--child .ds-feed__track{height:8px}.ds-feed__fill{display:block;height:100%;background:var(--cyan)}.ds-feed__fill--night{background:var(--indigo)}.ds-feed__fill--under{background:var(--amber-strong)}.ds-feed__fill--low{background:var(--amber-strong);opacity:.75}.ds-feed__fill--high{background:var(--green)}.ds-feed__volume{width:52px;text-align:right;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}.ds-feed--child .ds-feed__volume{font-weight:600;color:var(--muted)}.ds-feed__flag{width:22px;height:18px;display:inline-flex;align-items:center;justify-content:center}.ds-feed__flag .pi{font-size:12px;font-weight:700}.ds-feed__flag--low{color:var(--amber)}.ds-feed__flag--high{color:var(--green)}.ds-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--purple);border-radius:var(--r-card);padding:11px 13px}.ds-card__top{display:flex;align-items:center;gap:8px;margin-bottom:6px}.ds-card__place{font-size:11px;color:var(--faint)}.ds-card__title{font-size:14px;font-weight:700}.ds-card__detail{font-size:12px;color:var(--muted);margin-top:3px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;word-break:break-word}.ds-card__detail ::ng-deep p{margin:0;display:inline}.ds-card__detail ::ng-deep p:after{content:\" \"}.ds-card__detail ::ng-deep ul,.ds-card__detail ::ng-deep ol{margin:0;padding-left:1.1em}.ds-card__detail ::ng-deep br{display:none}.ds-event{display:flex;gap:12px;align-items:center;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--blue);border-radius:var(--r-card);padding:11px 13px}.ds-event__time{background:var(--blue);color:#fff;border-radius:var(--r-small);padding:6px 9px;font-size:13px;font-weight:800;white-space:nowrap}.ds-event__title{display:block;font-size:14px;font-weight:700}.ds-event__meta{display:block;font-size:12px;color:var(--muted);margin-top:2px}.ds-related{display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--pink);border-radius:var(--r-card);padding:10px 12px;cursor:pointer;color:var(--text);font-family:inherit}.ds-related--static{cursor:default}.ds-related__badge{min-width:62px;text-align:center;border:1px solid currentcolor;border-radius:var(--r-small);padding:5px 4px}.ds-related__days{display:block;font-size:15px;font-weight:800}.ds-related__unit{display:block;font-size:10px;font-weight:700}.ds-related__body{flex:1;min-width:0}.ds-related__title{display:block;font-size:13px;font-weight:700}.ds-related__meta{display:block;font-size:11px;color:var(--muted);margin-top:2px}.ds-related__chev{font-size:12px;color:var(--faint)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: SafeRichHtmlPipe, name: "safeRichHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21118
21312
  }
21119
21313
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DailySummaryComponent, decorators: [{
21120
21314
  type: Component,
21121
- args: [{ selector: 'app-daily-summary', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let s = summary();\n\n<div class=\"ds\">\n <!-- \u0110i\u1EC1u h\u01B0\u1EDBng ng\u00E0y -->\n <div class=\"ds-datebar\" [class.ds-datebar--weekend]=\"s.weekend\">\n <button type=\"button\" class=\"ds-datebar__nav\" (click)=\"shiftDay(-1)\" aria-label=\"Ng\u00E0y tr\u01B0\u1EDBc\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <label class=\"ds-datebar__center\">\n <span class=\"ds-datebar__label\">\n {{ s.weekday }}, {{ s.displayDate }}\n </span>\n @if (s.weekend) {\n <span class=\"ds-datebar__badge\">Cu\u1ED1i tu\u1EA7n</span>\n }\n <input\n type=\"date\"\n class=\"ds-datebar__input\"\n [value]=\"s.date\"\n [max]=\"maxDate\"\n (change)=\"onDateInput($any($event.target).value)\"\n aria-label=\"Ch\u1ECDn ng\u00E0y\"\n />\n </label>\n\n <button\n type=\"button\"\n class=\"ds-datebar__nav\"\n [disabled]=\"!canGoForward()\"\n (click)=\"shiftDay(1)\"\n aria-label=\"Ng\u00E0y sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @if (canGoForward()) {\n <button type=\"button\" class=\"ds-reset\" (click)=\"goLatest()\">\n <i class=\"pi pi-replay\" aria-hidden=\"true\"></i>\n V\u1EC1 ng\u00E0y m\u1EDBi nh\u1EA5t ({{ maxDateLabel }})\n </button>\n }\n\n <!-- T\u1ED5ng quan -->\n <section class=\"ds-hero\" aria-label=\"T\u1ED5ng quan trong ng\u00E0y\">\n <div class=\"ds-hero__top\">\n <div>\n <div class=\"ds-hero__label\">T\u1ED4NG L\u01AF\u1EE2NG S\u1EEEA TRONG NG\u00C0Y</div>\n <div class=\"ds-hero__total\">{{ s.totalMl }}<span class=\"ds-hero__unit\">ml</span></div>\n @if (s.hasFeeding || s.yesterday.totalMl > 0) {\n <div\n class=\"ds-hero__delta\"\n [class.ds-hero__delta--up]=\"s.yesterday.deltaMl > 0\"\n [class.ds-hero__delta--down]=\"s.yesterday.deltaMl < 0\"\n [class.ds-hero__delta--flat]=\"s.yesterday.deltaMl === 0\"\n >\n @if (s.yesterday.totalMl === 0 && s.hasFeeding) {\n H\u00F4m qua ch\u01B0a ghi nh\u1EADn c\u1EEF\n } @else if (s.yesterday.deltaMl === 0) {\n B\u1EB1ng h\u00F4m qua ({{ s.yesterday.totalMl }}ml)\n } @else {\n <i\n class=\"pi\"\n [class.pi-arrow-up]=\"s.yesterday.deltaMl > 0\"\n [class.pi-arrow-down]=\"s.yesterday.deltaMl < 0\"\n aria-hidden=\"true\"\n ></i>\n {{ s.yesterday.deltaMl > 0 ? '+' : '' }}{{ s.yesterday.deltaMl }}ml\n @if (s.yesterday.deltaPercent !== null) {\n <span class=\"ds-hero__delta-pct\">\n ({{ s.yesterday.deltaPercent > 0 ? '+' : '' }}{{ s.yesterday.deltaPercent }}%)\n </span>\n }\n so v\u1EDBi h\u00F4m qua\n }\n </div>\n }\n </div>\n <div class=\"ds-hero__right\">\n <div class=\"ds-hero__count\">{{ s.feedCount }} c\u1EEF b\u00FA</div>\n <div class=\"ds-hero__avg\">\n @if (s.feedCount > 0) {\n trung b\u00ECnh {{ s.avgMl }}ml m\u1ED7i c\u1EEF\n } @else {\n ch\u01B0a ghi nh\u1EADn c\u1EEF n\u00E0o\n }\n </div>\n </div>\n </div>\n\n <div class=\"ds-hero__stats\">\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF cao nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--green\">\n {{ s.hasFeeding ? s.maxMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF th\u1EA5p nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--blue\">\n {{ s.hasFeeding ? s.minMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">B\u00FA \u0111\u00EAm</span>\n <span\n class=\"ds-hero__stat-value\"\n [class.ds-hero__stat-value--indigo]=\"s.nightCount > 0\"\n [class.ds-hero__stat-value--green]=\"s.nightCount === 0\"\n >\n {{ s.nightCount > 0 ? s.nightCount + ' c\u1EEF' : 'Kh\u00F4ng c\u00F3' }}\n </span>\n </div>\n </div>\n </section>\n\n @if (s.nightCount > 0) {\n <div class=\"ds-night\">\n B\u00E9 c\u00F3 b\u00FA \u0111\u00EAm trong khung 00:00 \u2013 05:00: {{ s.nightCount }} c\u1EEF b\u00FA, t\u1ED5ng {{ s.nightMl }}ml\n </div>\n }\n\n <!-- D\u1EA3i hai tu\u1EA7n -->\n <section class=\"ds-strip\" aria-label=\"D\u1EA3i hai tu\u1EA7n\">\n <div class=\"ds-strip__frame\">\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n (click)=\"shiftStripWeek(-2)\"\n aria-label=\"Tu\u1EA7n tr\u01B0\u1EDBc\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"ds-strip__content\">\n <div class=\"ds-strip__chart\" aria-hidden=\"true\">\n @for (cell of s.strip; track cell.date) {\n <div\n class=\"ds-strip__bar-col\"\n [class.ds-strip__bar-col--current]=\"cell.current\"\n [class.ds-strip__bar-col--weekend]=\"cell.weekend\"\n >\n <div class=\"ds-strip__bar-track\">\n <span\n class=\"ds-strip__bar\"\n [class.ds-strip__bar--empty]=\"!cell.hasFeeding\"\n [style.height.%]=\"cell.barPercent\"\n ></span>\n </div>\n @if (cell.hasFeeding) {\n <span class=\"ds-strip__bar-label\">{{ cell.totalMl }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"ds-strip__row\">\n @for (cell of s.strip; track cell.date) {\n <button\n type=\"button\"\n class=\"ds-strip__cell\"\n [class.ds-strip__cell--current]=\"cell.current\"\n [class.ds-strip__cell--weekend]=\"cell.weekend\"\n [disabled]=\"!isNavigable(cell.date)\"\n (click)=\"openDate(cell.date)\"\n [attr.aria-current]=\"cell.current ? 'date' : null\"\n [attr.aria-label]=\"cell.weekdayShort + ' ng\u00E0y ' + cell.dayNumber\"\n >\n <span class=\"ds-strip__dow\">{{ cell.weekdayShort }}</span>\n <span class=\"ds-strip__day\" [class.ds-strip__day--data]=\"cell.hasFeeding\">\n {{ cell.dayNumber }}\n </span>\n <span\n class=\"ds-strip__dot\"\n [class.ds-strip__dot--activity]=\"cell.hasActivity\"\n [class.ds-strip__dot--event]=\"!cell.hasActivity && cell.hasEvent\"\n ></span>\n </button>\n }\n </div>\n </div>\n\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n [disabled]=\"!canShiftStripForward()\"\n (click)=\"shiftStripWeek(2)\"\n aria-label=\"Tu\u1EA7n sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"ds-strip__legend\">\n <span><i class=\"ds-strip__dot ds-strip__dot--activity\"></i> C\u00F3 ho\u1EA1t \u0111\u1ED9ng</span>\n <span><i class=\"ds-strip__dot ds-strip__dot--event\"></i> C\u00F3 l\u1ECBch h\u1EB9n</span>\n </div>\n </section>\n\n <!-- Th\u1EC3 tr\u1EA1ng & m\u1EE9c \u0111\u1EA1t khuy\u1EBFn ngh\u1ECB -->\n @if (s.body) {\n <section class=\"ds-body-card\" aria-label=\"Th\u1EC3 tr\u1EA1ng v\u00E0 khuy\u1EBFn ngh\u1ECB\">\n <div class=\"ds-body-card__title\">Th\u1EC3 tr\u1EA1ng &amp; khuy\u1EBFn ngh\u1ECB</div>\n\n <div class=\"ds-growth\">\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.weightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n C\u00E2n n\u1EB7ng\n </span>\n @if (s.body.weightWho; as ww) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"ww.status\">\n {{ ww.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">{{ s.body.weightKg }}<span>kg</span></div>\n\n @if (s.body.weightWho; as ww) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ ww.actual }}kg</span>\n <span>Trung v\u1ECB WHO {{ ww.median }}kg</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"ww.status\"\n [style.left.%]=\"whoMarkerPosition(ww.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ ww.percentile }} \u00B7 z {{ formatWhoZ(ww.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"ww.status\">\n {{ ww.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n @if (s.body.ageInDays !== null) {\n Th\u00EAm gi\u1EDBi t\u00EDnh trong profile \u0111\u1EC3 so chu\u1EA9n WHO\n } @else {\n C\u1EA7n ng\u00E0y sinh \u0111\u1EC3 so chu\u1EA9n WHO\n }\n </div>\n }\n </article>\n\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.heightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-arrows-v\" aria-hidden=\"true\"></i>\n Chi\u1EC1u cao\n </span>\n @if (s.body.heightWho; as hw) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"hw.status\">\n {{ hw.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">\n @if (s.body.heightCm) {\n {{ s.body.heightCm }}<span>cm</span>\n } @else {\n \u2014<span>cm</span>\n }\n </div>\n\n @if (s.body.heightWho; as hw) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ hw.actual }}cm</span>\n <span>Trung v\u1ECB WHO {{ hw.median }}cm</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"hw.status\"\n [style.left.%]=\"whoMarkerPosition(hw.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ hw.percentile }} \u00B7 z {{ formatWhoZ(hw.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"hw.status\">\n {{ hw.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n {{ s.body.heightCm ? 'Thi\u1EBFu ng\u00E0y sinh/gi\u1EDBi t\u00EDnh \u0111\u1EC3 so chu\u1EA9n' : 'Ch\u01B0a \u0111o chi\u1EC1u cao' }}\n </div>\n }\n </article>\n </div>\n\n <div class=\"ds-body-card__meta\">\n @if (s.body.daysFromViewedDay === 0) {\n C\u00E2n \u0111\u00FAng ng\u00E0y n\u00E0y\n } @else if (s.body.measuredAfter) {\n C\u00E2n ng\u00E0y {{ s.body.measuredDisplay }}, sau ng\u00E0y n\u00E0y {{ s.body.daysFromViewedDay }} ng\u00E0y\n } @else {\n C\u00E2n g\u1EA7n nh\u1EA5t tr\u01B0\u1EDBc \u0111\u00F3, ng\u00E0y {{ s.body.measuredDisplay }}\n }\n @if (s.body.heightDate && s.body.heightDate !== s.body.measuredDate) {\n \u00B7 \u0111o chi\u1EC1u cao ng\u00E0y {{ s.body.heightDisplay }}\n }\n </div>\n\n @if (s.nutrition) {\n <div class=\"ds-target\">\n <div class=\"ds-target__head\">\n <span class=\"ds-target__label\">L\u01B0\u1EE3ng s\u1EEFa so khuy\u1EBFn ngh\u1ECB</span>\n <span\n class=\"ds-body-card__percent\"\n [class.ds-body-card__percent--low]=\"s.nutrition.percentOfMin < 85\"\n [class.ds-body-card__percent--ok]=\"s.nutrition.percentOfMin >= 85\"\n >\n {{ s.nutrition.percentOfMin }}%\n </span>\n </div>\n <div class=\"ds-target__track\">\n <span\n class=\"ds-target__fill\"\n [class.ds-target__fill--low]=\"s.nutrition.percentOfMin < 85\"\n [style.width.%]=\"s.nutrition.barPercent\"\n ></span>\n </div>\n <div class=\"ds-target__scale\">\n <span>0 ml</span>\n <span>\n Khuy\u1EBFn ngh\u1ECB {{ s.nutrition.dailyMlMin }}\u2013{{ s.nutrition.dailyMlMax }} ml m\u1ED7i ng\u00E0y\n </span>\n </div>\n <div class=\"ds-target__note\">\n @if (s.nutrition.remainingToMin > 0) {\n B\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml, c\u00F2n thi\u1EBFu {{ s.nutrition.remainingToMin }}ml n\u1EEFa m\u1EDBi \u0111\u1EA1t\n m\u1EE9c t\u1ED1i thi\u1EC3u.\n } @else {\n {{ s.nutrition.statusLabel }} \u2014 b\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml trong ng\u00E0y.\n }\n </div>\n </div>\n } @else {\n <div class=\"ds-body-card__meta\">\n Ch\u01B0a t\u00EDnh \u0111\u01B0\u1EE3c khuy\u1EBFn ngh\u1ECB l\u01B0\u1EE3ng s\u1EEFa v\u00EC thi\u1EBFu ng\u00E0y sinh c\u1EE7a b\u00E9.\n </div>\n }\n </section>\n }\n\n <!-- Nh\u00ECn l\u1EA1i -->\n @if (s.recent.length > 0) {\n <section aria-label=\"Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc\">\n <div class=\"ds-head ds-head--pink\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"ds-head__count\">{{ s.recent.length }}</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Y t\u1EBF {{ lookBackDays }} ng\u00E0y g\u1EA7n \u0111\u00E2y</span>\n </div>\n <div class=\"ds-list\">\n @for (item of s.recent; track item.date + item.title) {\n <button\n type=\"button\"\n class=\"ds-related\"\n [style.border-left-color]=\"item.color\"\n (click)=\"openDate(item.date)\"\n >\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y tr\u01B0\u1EDBc</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n <i class=\"pi pi-chevron-right ds-related__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA b\u1ECB d\u00E3n -->\n @if (s.stretchedGaps.length > 0) {\n <section aria-label=\"C\u1EEF b\u00FA b\u1ECB d\u00E3n\">\n <div class=\"ds-head ds-head--orange\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA b\u1ECB d\u00E3n</span>\n <span class=\"ds-head__count\">{{ s.stretchedGaps.length }} l\u1EA7n</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Ng\u01B0\u1EE1ng {{ s.stretchThresholdHuman }}</span>\n </div>\n <div class=\"ds-list\">\n @for (gap of s.stretchedGaps; track gap.from + gap.to) {\n <div class=\"ds-gap\">\n <div class=\"ds-gap__row\">\n <span class=\"ds-gap__time\">{{ gap.from }}</span>\n <span class=\"ds-gap__track\">\n <span class=\"ds-gap__fill\" [style.width.%]=\"gap.barPercent\"></span>\n <span class=\"ds-gap__value\">{{ gap.humanGap }}</span>\n </span>\n <span class=\"ds-gap__time\">{{ gap.to }}</span>\n </div>\n <div class=\"ds-gap__foot\">\n <span class=\"ds-gap__note\">\n {{ gap.note || 'Kho\u1EA3ng c\u00E1ch d\u00E0i h\u01A1n nh\u1ECBp b\u00FA th\u01B0\u1EDDng ng\u00E0y' }}\n </span>\n <span class=\"ds-gap__volume\">{{ gap.volume }}ml</span>\n </div>\n </div>\n }\n <div class=\"ds-hint\">\n Kho\u1EA3ng c\u00E1ch trung b\u00ECnh h\u00F4m nay {{ s.avgGapHuman }}, d\u00E0i nh\u1EA5t {{ s.maxGapHuman }}\n </div>\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA -->\n <section aria-label=\"C\u1EEF b\u00FA\">\n <div class=\"ds-head ds-head--cyan\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA</span>\n @if (s.feedCount > 0) {\n <span class=\"ds-head__count\">{{ s.feedCount }} c\u1EEF b\u00FA</span>\n }\n </div>\n @if (!s.hasFeeding) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 c\u1EEF b\u00FA n\u00E0o \u0111\u01B0\u1EE3c ghi nh\u1EADn trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-feed-periods\">\n @for (period of s.feedPeriods; track period.id) {\n <div class=\"ds-period\" [attr.aria-label]=\"period.label\">\n <div class=\"ds-period__head\">\n <span class=\"ds-period__label\">{{ period.label }}</span>\n <span class=\"ds-period__meta\">\n {{ period.feedCount }} c\u1EEF \u00B7 {{ period.totalMl }}ml\n </span>\n </div>\n <div class=\"ds-feeds\">\n @for (feed of period.feeds; track feed.time + feed.volume + $index) {\n <div class=\"ds-feed-block\">\n <div class=\"ds-feed\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"feed.night\"\n [class.ds-feed__time--low]=\"feed.level === 'under' || feed.level === 'low'\"\n [class.ds-feed__time--high]=\"feed.level === 'high'\"\n >\n {{ feed.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"feed.night && feed.level === 'normal'\"\n [class.ds-feed__fill--under]=\"feed.level === 'under'\"\n [class.ds-feed__fill--low]=\"feed.level === 'low'\"\n [class.ds-feed__fill--high]=\"feed.level === 'high'\"\n [style.width.%]=\"feed.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ feed.volume }}ml</span>\n <span class=\"ds-feed__flag\">\n @if (feed.level === 'under' || feed.level === 'low') {\n <i\n class=\"pi pi-arrow-down ds-feed__flag--low\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n } @else if (feed.level === 'high') {\n <i\n class=\"pi pi-arrow-up ds-feed__flag--high\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n }\n </span>\n </div>\n @if (feed.memberCount > 1) {\n <div class=\"ds-feed__children\">\n @for (m of feed.members; track m.time + m.volume) {\n <div class=\"ds-feed ds-feed--child\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"m.night\"\n >\n {{ m.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"m.night\"\n [style.width.%]=\"m.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ m.volume }}ml</span>\n <span class=\"ds-feed__flag\" aria-hidden=\"true\"></span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n @if (lowFeedCount() > 0) {\n <div class=\"ds-hint ds-hint--amber\">\n {{ lowFeedCount() }} c\u1EEF \u00EDt h\u01A1n m\u1EE9c g\u1EE3i \u00FD\n @if (s.nutrition) {\n kho\u1EA3ng {{ s.nutrition.expectedPerFeed }}ml m\u1ED7i c\u1EEF v\u1EDBi {{ s.feedCount }} c\u1EEF trong ng\u00E0y\n } @else {\n so v\u1EDBi nh\u1ECBp b\u00FA chung c\u1EE7a ng\u00E0y\n }\n </div>\n }\n }\n </section>\n\n <!-- Ho\u1EA1t \u0111\u1ED9ng -->\n <section aria-label=\"Ho\u1EA1t \u0111\u1ED9ng\">\n <div class=\"ds-head ds-head--purple\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Ho\u1EA1t \u0111\u1ED9ng</span>\n @if (s.activities.length > 0) {\n <span class=\"ds-head__count\">{{ s.activities.length }}</span>\n }\n </div>\n @if (loadingMedical()) {\n <div class=\"ds-empty\">\u0110ang t\u1EA3i ho\u1EA1t \u0111\u1ED9ng y t\u1EBF\u2026</div>\n } @else if (s.activities.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 ho\u1EA1t \u0111\u1ED9ng n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (act of s.activities; track act.title) {\n <div class=\"ds-card\" [style.border-left-color]=\"act.kindColor\">\n <div class=\"ds-card__top\">\n <span\n class=\"ds-tag\"\n [style.color]=\"act.kindColor\"\n [style.border-color]=\"act.kindColor\"\n >\n <i class=\"fa-solid\" [ngClass]=\"act.kindIcon\" aria-hidden=\"true\"></i>\n {{ act.kindLabel }}\n </span>\n @if (act.place) {\n <span class=\"ds-card__place\">{{ act.place }}</span>\n }\n </div>\n <div class=\"ds-card__title\">{{ act.title }}</div>\n @if (act.detail) {\n <div class=\"ds-card__detail\">{{ act.detail }}</div>\n }\n </div>\n }\n </div>\n }\n </section>\n\n <!-- L\u1ECBch -->\n <section aria-label=\"L\u1ECBch\">\n <div class=\"ds-head ds-head--blue\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">L\u1ECBch</span>\n @if (s.events.length > 0) {\n <span class=\"ds-head__count\">{{ s.events.length }}</span>\n }\n </div>\n @if (s.events.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (ev of s.events; track ev.time + ev.title) {\n <div class=\"ds-event\">\n <span class=\"ds-event__time\">{{ ev.time }}</span>\n <span class=\"ds-event__body\">\n <span class=\"ds-event__title\">{{ ev.title }}</span>\n @if (ev.place) {\n <span class=\"ds-event__meta\">{{ ev.place }}</span>\n }\n @if (ev.note) {\n <span class=\"ds-event__meta\">{{ ev.note }}</span>\n }\n </span>\n </div>\n }\n </div>\n }\n </section>\n\n <!-- S\u1EAFp t\u1EDBi -->\n <section aria-label=\"S\u1EAFp t\u1EDBi\">\n <div class=\"ds-head ds-head--green\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">S\u1EAFp t\u1EDBi</span>\n @if (s.upcoming.length > 0) {\n <span class=\"ds-head__count\">{{ s.upcoming.length }}</span>\n }\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">{{ lookAheadDays }} ng\u00E0y t\u1EDBi</span>\n </div>\n @if (s.upcoming.length === 0) {\n <div class=\"ds-empty\">Ch\u01B0a c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong {{ lookAheadDays }} ng\u00E0y t\u1EDBi.</div>\n } @else {\n <div class=\"ds-list\">\n @for (item of s.upcoming; track item.date + item.title) {\n <div class=\"ds-related ds-related--static\" [style.border-left-color]=\"item.color\">\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y n\u1EEFa</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n </div>\n }\n </div>\n }\n </section>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f4f6fb;--surface: #ffffff;--border: #dfe5ef;--text: #0b1220;--muted: #5a6a85;--faint: #8c9ab1;--cyan: #0891b2;--cyan-soft: #dff7fd;--cyan-line: #7fdcee;--indigo: #5b5bf0;--indigo-soft: #e9e9ff;--indigo-line: #b4b4fb;--orange: #ef7314;--orange-soft: #fff0e2;--orange-line: #fbc088;--amber: #b45309;--amber-strong: #f59e0b;--amber-soft: #fef6e0;--amber-line: #f6cf7d;--purple: #8b3ff5;--blue: #2563eb;--green: #16a34a;--pink: #e0338c;--r-card: 10px;--r-small: 6px;display:block;background:var(--bg);color:var(--text)}.ds{display:flex;flex-direction:column;gap:18px;padding:14px 14px 32px}.ds-datebar{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:8px 10px}.ds-datebar--weekend{background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-datebar--weekend .ds-datebar__nav{color:var(--indigo)}.ds-datebar--weekend .ds-datebar__nav:disabled{color:var(--faint)}.ds-datebar--weekend .ds-datebar__label{color:var(--indigo)}.ds-datebar__nav{border:none;background:transparent;color:var(--cyan);font-size:15px;padding:6px 10px;cursor:pointer}.ds-datebar__nav:disabled{color:var(--faint);cursor:default}.ds-datebar__center{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:4px 8px;cursor:pointer;border-radius:var(--r-small)}.ds-datebar__label{font-size:15px;font-weight:800;color:var(--cyan);text-align:center;line-height:1.25}.ds-datebar__badge{font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase;color:var(--indigo);background:var(--surface);border:1px solid var(--indigo-line);border-radius:999px;padding:1px 7px}.ds-datebar__input{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:none;cursor:pointer}.ds-reset{align-self:center;margin-top:-12px;display:inline-flex;align-items:center;gap:6px;border:1px solid var(--cyan-line);background:var(--surface);color:var(--cyan);border-radius:999px;padding:6px 14px;font-family:inherit;font-size:12px;font-weight:700;cursor:pointer}.ds-reset i{font-size:12px}.ds-datebar--weekend~.ds-reset{border-color:var(--indigo-line);color:var(--indigo)}.ds-hero{background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:14px;color:var(--text)}.ds-hero__top{display:flex;align-items:flex-end;justify-content:space-between;gap:12px}.ds-hero__label{font-size:11px;font-weight:800;color:var(--cyan)}.ds-hero__total{font-size:38px;font-weight:800;line-height:1.05;margin-top:2px;color:var(--cyan)}.ds-hero__unit{font-size:15px;font-weight:700;margin-left:4px}.ds-hero__delta{display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px;margin-top:6px;font-size:12px;font-weight:700;color:var(--muted)}.ds-hero__delta i{font-size:11px}.ds-hero__delta--up{color:#047857}.ds-hero__delta--down{color:#b45309}.ds-hero__delta--flat{color:var(--muted)}.ds-hero__delta-pct{font-weight:800}.ds-hero__right{text-align:right}.ds-hero__count{font-size:18px;font-weight:800}.ds-hero__avg{font-size:11px;font-weight:600;color:var(--muted)}.ds-hero__stats{display:flex;gap:8px;margin-top:14px}.ds-hero__stat{flex:1;background:var(--surface);border:1px solid var(--cyan-line);border-radius:var(--r-small);padding:8px 10px}.ds-hero__stat-label{display:block;font-size:10px;font-weight:700;color:var(--faint)}.ds-hero__stat-value{display:block;font-size:14px;font-weight:800;margin-top:2px;color:var(--text)}.ds-hero__stat-value--green{color:var(--green)}.ds-hero__stat-value--blue{color:var(--blue)}.ds-hero__stat-value--indigo{color:var(--indigo)}.ds-body-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--green);border-radius:var(--r-card);padding:12px 14px}.ds-body-card__title{font-size:12px;font-weight:800;letter-spacing:.02em;color:var(--muted);margin-bottom:10px}.ds-growth{display:grid;grid-template-columns:1fr 1fr;gap:10px}@media(max-width:420px){.ds-growth{grid-template-columns:1fr}}.ds-growth__card{background:var(--bg);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 11px 11px;min-width:0}.ds-growth__card[data-status=normal]{border-color:#86efac;background:#f0fdf4}.ds-growth__card[data-status=low],.ds-growth__card[data-status=very_low]{border-color:var(--amber-line);background:var(--amber-soft)}.ds-growth__card[data-status=high],.ds-growth__card[data-status=very_high]{border-color:#93c5fd;background:#eff6ff}.ds-growth__head{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-bottom:4px}.ds-growth__label{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:800;color:var(--muted)}.ds-growth__label i{font-size:11px;color:var(--green)}.ds-growth__badge{font-size:10px;font-weight:800;border-radius:999px;padding:2px 7px;white-space:nowrap;background:var(--surface);border:1px solid var(--border);color:var(--muted)}.ds-growth__badge[data-status=normal]{color:var(--green);border-color:#86efac}.ds-growth__badge[data-status=low],.ds-growth__badge[data-status=very_low]{color:var(--amber);border-color:var(--amber-line)}.ds-growth__badge[data-status=high],.ds-growth__badge[data-status=very_high]{color:var(--blue);border-color:#93c5fd}.ds-growth__value{font-size:24px;font-weight:800;line-height:1.1;font-variant-numeric:tabular-nums}.ds-growth__value span{font-size:12px;font-weight:700;margin-left:3px;color:var(--muted)}.ds-growth__compare{display:flex;flex-direction:column;gap:1px;margin-top:6px;font-size:10px;font-weight:600;color:var(--faint)}.ds-growth__scale{margin-top:8px}.ds-growth__scale-labels{display:flex;justify-content:space-between;font-size:9px;font-weight:700;color:var(--faint);margin-bottom:3px}.ds-growth__scale-mid{color:var(--green)}.ds-growth__scale-track{position:relative;display:flex;height:10px;border-radius:5px;overflow:hidden;background:var(--border)}.ds-growth__band{display:block;height:100%}.ds-growth__band--low{width:25%;background:#fcd34d}.ds-growth__band--mid{width:50%;background:#86efac}.ds-growth__band--high{width:25%;background:#93c5fd}.ds-growth__marker{position:absolute;top:50%;width:12px;height:12px;margin-left:-6px;margin-top:-6px;border-radius:50%;background:var(--text);border:2px solid #ffffff;box-sizing:border-box}.ds-growth__marker[data-status=normal]{background:var(--green)}.ds-growth__marker[data-status=low],.ds-growth__marker[data-status=very_low]{background:var(--amber)}.ds-growth__marker[data-status=high],.ds-growth__marker[data-status=very_high]{background:var(--blue)}.ds-growth__scale-legend{display:flex;justify-content:space-between;margin-top:3px;font-size:9px;font-weight:700;color:var(--faint);font-variant-numeric:tabular-nums}.ds-growth__delta{margin-top:7px;font-size:11px;font-weight:700;color:var(--green)}.ds-growth__delta[data-status=low],.ds-growth__delta[data-status=very_low]{color:var(--amber)}.ds-growth__delta[data-status=high],.ds-growth__delta[data-status=very_high]{color:var(--blue)}.ds-growth__delta[data-status=unknown]{color:var(--muted)}.ds-growth__empty{margin-top:8px;font-size:11px;font-weight:600;color:var(--faint);line-height:1.4}.ds-body-card__percent{font-size:18px;font-weight:800;color:var(--green)}.ds-body-card__percent--low{color:var(--amber)}.ds-body-card__meta{font-size:11px;color:var(--faint);margin-top:8px}.ds-target{margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}.ds-target__head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.ds-target__label{font-size:11px;font-weight:800;color:var(--muted)}.ds-target__track{height:10px;background:var(--bg);border-radius:3px;overflow:hidden}.ds-target__fill{display:block;height:100%;background:var(--green)}.ds-target__fill--low{background:var(--amber-strong)}.ds-target__scale{display:flex;justify-content:space-between;gap:10px;font-size:10px;color:var(--faint);margin-top:4px}.ds-target__note{font-size:12px;color:var(--muted);margin-top:6px}.ds-night{background:var(--indigo-soft);border:1px solid var(--indigo-line);border-left:4px solid var(--indigo);border-radius:var(--r-card);padding:10px 12px;font-size:12px;font-weight:700;color:var(--indigo)}.ds-strip{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 6px 8px}.ds-strip__frame{display:flex;align-items:stretch;gap:2px}.ds-strip__nav{flex-shrink:0;align-self:center;width:26px;height:52px;border:1px solid var(--border);border-radius:8px;background:#f8fafc;color:var(--cyan);font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0}.ds-strip__nav:disabled{color:var(--faint);cursor:default;opacity:.45}.ds-strip__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.ds-strip__chart{display:flex;gap:1px;align-items:flex-end;height:56px;padding:0 0 2px}.ds-strip__bar-col{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:2px;height:100%}.ds-strip__bar-track{flex:1;width:100%;max-width:18px;display:flex;align-items:flex-end;justify-content:center}.ds-strip__bar{width:100%;min-height:3px;border-radius:4px 4px 2px 2px;background:linear-gradient(180deg,#22d3ee,var(--cyan));transition:height .2s ease}.ds-strip__bar--empty{min-height:2px;height:2px!important;background:#e2e8f0;opacity:.7}.ds-strip__bar-col--current .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#34d399,#059669)}.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#a5b4fc,var(--indigo))}.ds-strip__bar-col--current.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#818cf8,#4f46e5)}.ds-strip__bar-label{font-size:8px;font-weight:800;color:var(--muted);line-height:1;font-variant-numeric:tabular-nums}.ds-strip__bar-col--current .ds-strip__bar-label{color:var(--cyan)}.ds-strip__row{display:flex;gap:1px}.ds-strip__cell{flex:1;min-width:0;border:none;background:transparent;padding:2px 0 0;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px;font-family:inherit}.ds-strip__cell:disabled{cursor:default;opacity:.4}.ds-strip__dow{font-size:9px;font-weight:700;color:var(--faint)}.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__day{width:22px;height:22px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:var(--faint);border:1px solid transparent}.ds-strip__day--data{color:var(--text);border-color:var(--border)}.ds-strip__cell--current .ds-strip__dow{color:var(--cyan)}.ds-strip__cell--current .ds-strip__day{background:var(--cyan);border-color:var(--cyan);color:#fff}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__day{background:var(--indigo);border-color:var(--indigo)}.ds-strip__dot{width:4px;height:4px;border-radius:2px;background:transparent;display:inline-block}.ds-strip__dot--activity{background:var(--purple)}.ds-strip__dot--event{background:var(--blue)}.ds-strip__legend{display:flex;gap:12px;margin-top:6px;padding-left:2px;font-size:10px;color:var(--muted)}.ds-strip__legend span{display:inline-flex;align-items:center;gap:5px}.ds-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;--tone: var(--cyan)}.ds-head--cyan{--tone: var(--cyan)}.ds-head--orange{--tone: var(--orange)}.ds-head--purple{--tone: var(--purple)}.ds-head--blue{--tone: var(--blue)}.ds-head--green{--tone: var(--green)}.ds-head--pink{--tone: var(--pink)}.ds-head__bar{width:4px;height:16px;border-radius:1px;background:var(--tone)}.ds-head__label{font-size:14px;font-weight:700}.ds-head__count{font-size:11px;font-weight:700;color:#fff;background:var(--tone);border-radius:var(--r-small);padding:2px 7px}.ds-head__spacer{flex:1}.ds-head__action{font-size:11px;font-weight:700;color:var(--tone)}.ds-list{display:flex;flex-direction:column;gap:8px}.ds-empty{background:var(--surface);border:1px dashed var(--border);border-radius:var(--r-card);padding:12px 14px;font-size:12px;color:var(--faint)}.ds-hint{font-size:11px;color:var(--faint)}.ds-hint--amber{color:var(--amber);font-weight:700;margin-top:8px}.ds-tag{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:700;border:1px solid var(--border);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-tag--indigo{color:var(--indigo);background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-tag--amber{color:var(--amber);background:var(--amber-soft);border-color:var(--amber-line)}.ds-tag--cyan{color:var(--cyan);background:var(--cyan-soft);border-color:var(--cyan-line)}.ds-gap{background:var(--surface);border:1px solid var(--orange-line);border-left:4px solid var(--orange);border-radius:var(--r-card);padding:11px 14px}.ds-gap__row{display:flex;align-items:center;gap:10px}.ds-gap__time{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums}.ds-gap__track{position:relative;flex:1;height:20px;display:block}.ds-gap__track:before{content:\"\";position:absolute;top:9px;left:0;right:0;height:3px;background:var(--orange-soft)}.ds-gap__fill{position:absolute;top:9px;left:0;height:3px;background:var(--orange)}.ds-gap__value{position:absolute;top:-1px;left:50%;transform:translate(-50%);font-size:11px;font-weight:800;color:#fff;background:var(--orange);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-gap__foot{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:8px}.ds-gap__note{font-size:12px;color:var(--muted)}.ds-gap__volume{font-size:12px;font-weight:700;white-space:nowrap}.ds-feed-periods{display:flex;flex-direction:column;gap:14px}.ds-period__head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:6px;padding:0 2px}.ds-period__label{font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--cyan)}.ds-period__meta{font-size:11px;font-weight:600;color:var(--faint)}.ds-feeds{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:12px;display:flex;flex-direction:column;gap:10px}.ds-feed-block{display:flex;flex-direction:column;gap:5px}.ds-feed{display:grid;grid-template-columns:42px minmax(0,1fr) 52px 22px;align-items:center;column-gap:10px}.ds-feed--child{grid-template-columns:42px minmax(0,1fr) 52px 22px;opacity:.92}.ds-feed__children{display:flex;flex-direction:column;gap:5px;padding-left:10px;border-left:2px solid var(--cyan-line);margin-left:6px}.ds-feed__time{width:42px;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums}.ds-feed__time--night{color:var(--indigo)}.ds-feed__time--low{color:var(--amber)}.ds-feed__time--high{color:var(--green)}.ds-feed--child .ds-feed__time{font-weight:600;color:var(--muted)}.ds-feed--child .ds-feed__time.ds-feed__time--night{color:var(--indigo)}.ds-feed__track{height:12px;background:var(--bg);border-radius:3px;overflow:hidden;display:block;min-width:0}.ds-feed--child .ds-feed__track{height:8px}.ds-feed__fill{display:block;height:100%;background:var(--cyan)}.ds-feed__fill--night{background:var(--indigo)}.ds-feed__fill--under{background:var(--amber-strong)}.ds-feed__fill--low{background:var(--amber-strong);opacity:.75}.ds-feed__fill--high{background:var(--green)}.ds-feed__volume{width:52px;text-align:right;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}.ds-feed--child .ds-feed__volume{font-weight:600;color:var(--muted)}.ds-feed__flag{width:22px;height:18px;display:inline-flex;align-items:center;justify-content:center}.ds-feed__flag .pi{font-size:12px;font-weight:700}.ds-feed__flag--low{color:var(--amber)}.ds-feed__flag--high{color:var(--green)}.ds-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--purple);border-radius:var(--r-card);padding:11px 13px}.ds-card__top{display:flex;align-items:center;gap:8px;margin-bottom:6px}.ds-card__place{font-size:11px;color:var(--faint)}.ds-card__title{font-size:14px;font-weight:700}.ds-card__detail{font-size:12px;color:var(--muted);margin-top:3px}.ds-event{display:flex;gap:12px;align-items:center;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--blue);border-radius:var(--r-card);padding:11px 13px}.ds-event__time{background:var(--blue);color:#fff;border-radius:var(--r-small);padding:6px 9px;font-size:13px;font-weight:800;white-space:nowrap}.ds-event__title{display:block;font-size:14px;font-weight:700}.ds-event__meta{display:block;font-size:12px;color:var(--muted);margin-top:2px}.ds-related{display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--pink);border-radius:var(--r-card);padding:10px 12px;cursor:pointer;color:var(--text);font-family:inherit}.ds-related--static{cursor:default}.ds-related__badge{min-width:62px;text-align:center;border:1px solid currentcolor;border-radius:var(--r-small);padding:5px 4px}.ds-related__days{display:block;font-size:15px;font-weight:800}.ds-related__unit{display:block;font-size:10px;font-weight:700}.ds-related__body{flex:1;min-width:0}.ds-related__title{display:block;font-size:13px;font-weight:700}.ds-related__meta{display:block;font-size:11px;color:var(--muted);margin-top:2px}.ds-related__chev{font-size:12px;color:var(--faint)}\n"] }]
21315
+ args: [{ selector: 'app-daily-summary', standalone: true, imports: [CommonModule, SafeRichHtmlPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let s = summary();\n\n<div class=\"ds\">\n <!-- \u0110i\u1EC1u h\u01B0\u1EDBng ng\u00E0y -->\n <div class=\"ds-datebar\" [class.ds-datebar--weekend]=\"s.weekend\">\n <button type=\"button\" class=\"ds-datebar__nav\" (click)=\"shiftDay(-1)\" aria-label=\"Ng\u00E0y tr\u01B0\u1EDBc\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <label class=\"ds-datebar__center\">\n <span class=\"ds-datebar__label\">\n {{ s.weekday }}, {{ s.displayDate }}\n </span>\n @if (s.weekend) {\n <span class=\"ds-datebar__badge\">Cu\u1ED1i tu\u1EA7n</span>\n }\n <input\n type=\"date\"\n class=\"ds-datebar__input\"\n [value]=\"s.date\"\n [max]=\"maxDate\"\n (change)=\"onDateInput($any($event.target).value)\"\n aria-label=\"Ch\u1ECDn ng\u00E0y\"\n />\n </label>\n\n <button\n type=\"button\"\n class=\"ds-datebar__nav\"\n [disabled]=\"!canGoForward()\"\n (click)=\"shiftDay(1)\"\n aria-label=\"Ng\u00E0y sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n @if (canGoForward()) {\n <button type=\"button\" class=\"ds-reset\" (click)=\"goLatest()\">\n <i class=\"pi pi-replay\" aria-hidden=\"true\"></i>\n V\u1EC1 ng\u00E0y m\u1EDBi nh\u1EA5t ({{ maxDateLabel }})\n </button>\n }\n\n <!-- T\u1ED5ng quan -->\n <section class=\"ds-hero\" aria-label=\"T\u1ED5ng quan trong ng\u00E0y\">\n <div class=\"ds-hero__top\">\n <div>\n <div class=\"ds-hero__label\">T\u1ED4NG L\u01AF\u1EE2NG S\u1EEEA TRONG NG\u00C0Y</div>\n <div class=\"ds-hero__total\">{{ s.totalMl }}<span class=\"ds-hero__unit\">ml</span></div>\n @if (s.hasFeeding || s.yesterday.totalMl > 0) {\n <div\n class=\"ds-hero__delta\"\n [class.ds-hero__delta--up]=\"s.yesterday.deltaMl > 0\"\n [class.ds-hero__delta--down]=\"s.yesterday.deltaMl < 0\"\n [class.ds-hero__delta--flat]=\"s.yesterday.deltaMl === 0\"\n >\n @if (s.yesterday.totalMl === 0 && s.hasFeeding) {\n H\u00F4m qua ch\u01B0a ghi nh\u1EADn c\u1EEF\n } @else if (s.yesterday.deltaMl === 0) {\n B\u1EB1ng h\u00F4m qua ({{ s.yesterday.totalMl }}ml)\n } @else {\n <i\n class=\"pi\"\n [class.pi-arrow-up]=\"s.yesterday.deltaMl > 0\"\n [class.pi-arrow-down]=\"s.yesterday.deltaMl < 0\"\n aria-hidden=\"true\"\n ></i>\n {{ s.yesterday.deltaMl > 0 ? '+' : '' }}{{ s.yesterday.deltaMl }}ml\n @if (s.yesterday.deltaPercent !== null) {\n <span class=\"ds-hero__delta-pct\">\n ({{ s.yesterday.deltaPercent > 0 ? '+' : '' }}{{ s.yesterday.deltaPercent }}%)\n </span>\n }\n so v\u1EDBi h\u00F4m qua\n }\n </div>\n }\n </div>\n <div class=\"ds-hero__right\">\n <div class=\"ds-hero__count\">{{ s.feedCount }} c\u1EEF b\u00FA</div>\n <div class=\"ds-hero__avg\">\n @if (s.feedCount > 0) {\n trung b\u00ECnh {{ s.avgMl }}ml m\u1ED7i c\u1EEF\n } @else {\n ch\u01B0a ghi nh\u1EADn c\u1EEF n\u00E0o\n }\n </div>\n </div>\n </div>\n\n <div class=\"ds-hero__stats\">\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF cao nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--green\">\n {{ s.hasFeeding ? s.maxMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">C\u1EEF th\u1EA5p nh\u1EA5t</span>\n <span class=\"ds-hero__stat-value ds-hero__stat-value--blue\">\n {{ s.hasFeeding ? s.minMl + 'ml' : '\u2014' }}\n </span>\n </div>\n <div class=\"ds-hero__stat\">\n <span class=\"ds-hero__stat-label\">B\u00FA \u0111\u00EAm</span>\n <span\n class=\"ds-hero__stat-value\"\n [class.ds-hero__stat-value--indigo]=\"s.nightCount > 0\"\n [class.ds-hero__stat-value--green]=\"s.nightCount === 0\"\n >\n {{ s.nightCount > 0 ? s.nightCount + ' c\u1EEF' : 'Kh\u00F4ng c\u00F3' }}\n </span>\n </div>\n </div>\n </section>\n\n @if (s.nightCount > 0) {\n <div class=\"ds-night\">\n B\u00E9 c\u00F3 b\u00FA \u0111\u00EAm trong khung 00:00 \u2013 05:00: {{ s.nightCount }} c\u1EEF b\u00FA, t\u1ED5ng {{ s.nightMl }}ml\n </div>\n }\n\n <!-- D\u1EA3i hai tu\u1EA7n -->\n <section class=\"ds-strip\" aria-label=\"D\u1EA3i hai tu\u1EA7n\">\n <div class=\"ds-strip__frame\">\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n (click)=\"shiftStripWeek(-2)\"\n aria-label=\"Tu\u1EA7n tr\u01B0\u1EDBc\"\n >\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"ds-strip__content\">\n <div class=\"ds-strip__chart\" aria-hidden=\"true\">\n @for (cell of s.strip; track cell.date) {\n <div\n class=\"ds-strip__bar-col\"\n [class.ds-strip__bar-col--current]=\"cell.current\"\n [class.ds-strip__bar-col--weekend]=\"cell.weekend\"\n >\n <div class=\"ds-strip__bar-track\">\n <span\n class=\"ds-strip__bar\"\n [class.ds-strip__bar--empty]=\"!cell.hasFeeding\"\n [style.height.%]=\"cell.barPercent\"\n ></span>\n </div>\n @if (cell.hasFeeding) {\n <span class=\"ds-strip__bar-label\">{{ cell.totalMl }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"ds-strip__row\">\n @for (cell of s.strip; track cell.date) {\n <button\n type=\"button\"\n class=\"ds-strip__cell\"\n [class.ds-strip__cell--current]=\"cell.current\"\n [class.ds-strip__cell--weekend]=\"cell.weekend\"\n [disabled]=\"!isNavigable(cell.date)\"\n (click)=\"openDate(cell.date)\"\n [attr.aria-current]=\"cell.current ? 'date' : null\"\n [attr.aria-label]=\"cell.weekdayShort + ' ng\u00E0y ' + cell.dayNumber\"\n >\n <span class=\"ds-strip__dow\">{{ cell.weekdayShort }}</span>\n <span class=\"ds-strip__day\" [class.ds-strip__day--data]=\"cell.hasFeeding\">\n {{ cell.dayNumber }}\n </span>\n <span\n class=\"ds-strip__dot\"\n [class.ds-strip__dot--activity]=\"cell.hasActivity\"\n [class.ds-strip__dot--event]=\"!cell.hasActivity && cell.hasEvent\"\n ></span>\n </button>\n }\n </div>\n </div>\n\n <button\n type=\"button\"\n class=\"ds-strip__nav\"\n [disabled]=\"!canShiftStripForward()\"\n (click)=\"shiftStripWeek(2)\"\n aria-label=\"Tu\u1EA7n sau\"\n >\n <i class=\"pi pi-chevron-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"ds-strip__legend\">\n <span><i class=\"ds-strip__dot ds-strip__dot--activity\"></i> C\u00F3 ho\u1EA1t \u0111\u1ED9ng</span>\n <span><i class=\"ds-strip__dot ds-strip__dot--event\"></i> C\u00F3 l\u1ECBch h\u1EB9n</span>\n </div>\n </section>\n\n <!-- Th\u1EC3 tr\u1EA1ng & m\u1EE9c \u0111\u1EA1t khuy\u1EBFn ngh\u1ECB -->\n @if (s.body) {\n <section class=\"ds-body-card\" aria-label=\"Th\u1EC3 tr\u1EA1ng v\u00E0 khuy\u1EBFn ngh\u1ECB\">\n <div class=\"ds-body-card__title\">Th\u1EC3 tr\u1EA1ng &amp; khuy\u1EBFn ngh\u1ECB</div>\n\n <div class=\"ds-growth\">\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.weightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-heart-fill\" aria-hidden=\"true\"></i>\n C\u00E2n n\u1EB7ng\n </span>\n @if (s.body.weightWho; as ww) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"ww.status\">\n {{ ww.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">{{ s.body.weightKg }}<span>kg</span></div>\n\n @if (s.body.weightWho; as ww) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ ww.actual }}kg</span>\n <span>Trung v\u1ECB WHO {{ ww.median }}kg</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"ww.status\"\n [style.left.%]=\"whoMarkerPosition(ww.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ ww.percentile }} \u00B7 z {{ formatWhoZ(ww.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"ww.status\">\n {{ ww.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n @if (s.body.ageInDays !== null) {\n Th\u00EAm gi\u1EDBi t\u00EDnh trong profile \u0111\u1EC3 so chu\u1EA9n WHO\n } @else {\n C\u1EA7n ng\u00E0y sinh \u0111\u1EC3 so chu\u1EA9n WHO\n }\n </div>\n }\n </article>\n\n <article\n class=\"ds-growth__card\"\n [attr.data-status]=\"s.body.heightWho?.status ?? 'unknown'\"\n >\n <div class=\"ds-growth__head\">\n <span class=\"ds-growth__label\">\n <i class=\"pi pi-arrows-v\" aria-hidden=\"true\"></i>\n Chi\u1EC1u cao\n </span>\n @if (s.body.heightWho; as hw) {\n <span class=\"ds-growth__badge\" [attr.data-status]=\"hw.status\">\n {{ hw.badgeLabel }}\n </span>\n }\n </div>\n <div class=\"ds-growth__value\">\n @if (s.body.heightCm) {\n {{ s.body.heightCm }}<span>cm</span>\n } @else {\n \u2014<span>cm</span>\n }\n </div>\n\n @if (s.body.heightWho; as hw) {\n <div class=\"ds-growth__compare\">\n <span>Hi\u1EC7n t\u1EA1i {{ hw.actual }}cm</span>\n <span>Trung v\u1ECB WHO {{ hw.median }}cm</span>\n </div>\n <div class=\"ds-growth__scale\" aria-hidden=\"true\">\n <div class=\"ds-growth__scale-labels\">\n <span>Th\u1EA5p</span>\n <span class=\"ds-growth__scale-mid\">Ph\u00F9 h\u1EE3p</span>\n <span>Cao</span>\n </div>\n <div class=\"ds-growth__scale-track\">\n <span class=\"ds-growth__band ds-growth__band--low\"></span>\n <span class=\"ds-growth__band ds-growth__band--mid\"></span>\n <span class=\"ds-growth__band ds-growth__band--high\"></span>\n <span\n class=\"ds-growth__marker\"\n [attr.data-status]=\"hw.status\"\n [style.left.%]=\"whoMarkerPosition(hw.zScore)\"\n ></span>\n </div>\n <div class=\"ds-growth__scale-legend\">\n <span>\u22122 SD</span>\n <span>P{{ hw.percentile }} \u00B7 z {{ formatWhoZ(hw.zScore) }}</span>\n <span>+2 SD</span>\n </div>\n </div>\n <div class=\"ds-growth__delta\" [attr.data-status]=\"hw.status\">\n {{ hw.deltaLabel }}\n </div>\n } @else {\n <div class=\"ds-growth__empty\">\n {{ s.body.heightCm ? 'Thi\u1EBFu ng\u00E0y sinh/gi\u1EDBi t\u00EDnh \u0111\u1EC3 so chu\u1EA9n' : 'Ch\u01B0a \u0111o chi\u1EC1u cao' }}\n </div>\n }\n </article>\n </div>\n\n <div class=\"ds-body-card__meta\">\n @if (s.body.daysFromViewedDay === 0) {\n C\u00E2n \u0111\u00FAng ng\u00E0y n\u00E0y\n } @else if (s.body.measuredAfter) {\n C\u00E2n ng\u00E0y {{ s.body.measuredDisplay }}, sau ng\u00E0y n\u00E0y {{ s.body.daysFromViewedDay }} ng\u00E0y\n } @else {\n C\u00E2n g\u1EA7n nh\u1EA5t tr\u01B0\u1EDBc \u0111\u00F3, ng\u00E0y {{ s.body.measuredDisplay }}\n }\n @if (s.body.heightDate && s.body.heightDate !== s.body.measuredDate) {\n \u00B7 \u0111o chi\u1EC1u cao ng\u00E0y {{ s.body.heightDisplay }}\n }\n </div>\n\n @if (s.nutrition) {\n <div class=\"ds-target\">\n <div class=\"ds-target__head\">\n <span class=\"ds-target__label\">L\u01B0\u1EE3ng s\u1EEFa so khuy\u1EBFn ngh\u1ECB</span>\n <span\n class=\"ds-body-card__percent\"\n [class.ds-body-card__percent--low]=\"s.nutrition.percentOfMin < 85\"\n [class.ds-body-card__percent--ok]=\"s.nutrition.percentOfMin >= 85\"\n >\n {{ s.nutrition.percentOfMin }}%\n </span>\n </div>\n <div class=\"ds-target__track\">\n <span\n class=\"ds-target__fill\"\n [class.ds-target__fill--low]=\"s.nutrition.percentOfMin < 85\"\n [style.width.%]=\"s.nutrition.barPercent\"\n ></span>\n </div>\n <div class=\"ds-target__scale\">\n <span>0 ml</span>\n <span>\n Khuy\u1EBFn ngh\u1ECB {{ s.nutrition.dailyMlMin }}\u2013{{ s.nutrition.dailyMlMax }} ml m\u1ED7i ng\u00E0y\n </span>\n </div>\n <div class=\"ds-target__note\">\n @if (s.nutrition.remainingToMin > 0) {\n B\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml, c\u00F2n thi\u1EBFu {{ s.nutrition.remainingToMin }}ml n\u1EEFa m\u1EDBi \u0111\u1EA1t\n m\u1EE9c t\u1ED1i thi\u1EC3u.\n } @else {\n {{ s.nutrition.statusLabel }} \u2014 b\u00E9 \u0111\u00E3 b\u00FA {{ s.totalMl }}ml trong ng\u00E0y.\n }\n </div>\n </div>\n } @else {\n <div class=\"ds-body-card__meta\">\n Ch\u01B0a t\u00EDnh \u0111\u01B0\u1EE3c khuy\u1EBFn ngh\u1ECB l\u01B0\u1EE3ng s\u1EEFa v\u00EC thi\u1EBFu ng\u00E0y sinh c\u1EE7a b\u00E9.\n </div>\n }\n </section>\n }\n\n <!-- Nh\u00ECn l\u1EA1i -->\n @if (s.recent.length > 0) {\n <section aria-label=\"Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc\">\n <div class=\"ds-head ds-head--pink\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Nh\u00ECn l\u1EA1i v\u00E0i ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"ds-head__count\">{{ s.recent.length }}</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Y t\u1EBF {{ lookBackDays }} ng\u00E0y g\u1EA7n \u0111\u00E2y</span>\n </div>\n <div class=\"ds-list\">\n @for (item of s.recent; track item.date + item.title) {\n <button\n type=\"button\"\n class=\"ds-related\"\n [style.border-left-color]=\"item.color\"\n (click)=\"openDate(item.date)\"\n >\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y tr\u01B0\u1EDBc</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n <i class=\"pi pi-chevron-right ds-related__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA b\u1ECB d\u00E3n -->\n @if (s.stretchedGaps.length > 0) {\n <section aria-label=\"C\u1EEF b\u00FA b\u1ECB d\u00E3n\">\n <div class=\"ds-head ds-head--orange\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA b\u1ECB d\u00E3n</span>\n <span class=\"ds-head__count\">{{ s.stretchedGaps.length }} l\u1EA7n</span>\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">Ng\u01B0\u1EE1ng {{ s.stretchThresholdHuman }}</span>\n </div>\n <div class=\"ds-list\">\n @for (gap of s.stretchedGaps; track gap.from + gap.to) {\n <div class=\"ds-gap\">\n <div class=\"ds-gap__row\">\n <span class=\"ds-gap__time\">{{ gap.from }}</span>\n <span class=\"ds-gap__track\">\n <span class=\"ds-gap__fill\" [style.width.%]=\"gap.barPercent\"></span>\n <span class=\"ds-gap__value\">{{ gap.humanGap }}</span>\n </span>\n <span class=\"ds-gap__time\">{{ gap.to }}</span>\n </div>\n <div class=\"ds-gap__foot\">\n <span class=\"ds-gap__note\">\n {{ gap.note || 'Kho\u1EA3ng c\u00E1ch d\u00E0i h\u01A1n nh\u1ECBp b\u00FA th\u01B0\u1EDDng ng\u00E0y' }}\n </span>\n <span class=\"ds-gap__volume\">{{ gap.volume }}ml</span>\n </div>\n </div>\n }\n <div class=\"ds-hint\">\n Kho\u1EA3ng c\u00E1ch trung b\u00ECnh h\u00F4m nay {{ s.avgGapHuman }}, d\u00E0i nh\u1EA5t {{ s.maxGapHuman }}\n </div>\n </div>\n </section>\n }\n\n <!-- C\u1EEF b\u00FA -->\n <section aria-label=\"C\u1EEF b\u00FA\">\n <div class=\"ds-head ds-head--cyan\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">C\u1EEF b\u00FA</span>\n @if (s.feedCount > 0) {\n <span class=\"ds-head__count\">{{ s.feedCount }} c\u1EEF b\u00FA</span>\n }\n </div>\n @if (!s.hasFeeding) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 c\u1EEF b\u00FA n\u00E0o \u0111\u01B0\u1EE3c ghi nh\u1EADn trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-feed-periods\">\n @for (period of s.feedPeriods; track period.id) {\n <div class=\"ds-period\" [attr.aria-label]=\"period.label\">\n <div class=\"ds-period__head\">\n <span class=\"ds-period__label\">{{ period.label }}</span>\n <span class=\"ds-period__meta\">\n {{ period.feedCount }} c\u1EEF \u00B7 {{ period.totalMl }}ml\n </span>\n </div>\n <div class=\"ds-feeds\">\n @for (feed of period.feeds; track feed.time + feed.volume + $index) {\n <div class=\"ds-feed-block\">\n <div class=\"ds-feed\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"feed.night\"\n [class.ds-feed__time--low]=\"feed.level === 'under' || feed.level === 'low'\"\n [class.ds-feed__time--high]=\"feed.level === 'high'\"\n >\n {{ feed.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"feed.night && feed.level === 'normal'\"\n [class.ds-feed__fill--under]=\"feed.level === 'under'\"\n [class.ds-feed__fill--low]=\"feed.level === 'low'\"\n [class.ds-feed__fill--high]=\"feed.level === 'high'\"\n [style.width.%]=\"feed.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ feed.volume }}ml</span>\n <span class=\"ds-feed__flag\">\n @if (feed.level === 'under' || feed.level === 'low') {\n <i\n class=\"pi pi-arrow-down ds-feed__flag--low\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n } @else if (feed.level === 'high') {\n <i\n class=\"pi pi-arrow-up ds-feed__flag--high\"\n [attr.title]=\"feed.levelLabel\"\n [attr.aria-label]=\"feed.levelLabel\"\n ></i>\n }\n </span>\n </div>\n @if (feed.memberCount > 1) {\n <div class=\"ds-feed__children\">\n @for (m of feed.members; track m.time + m.volume) {\n <div class=\"ds-feed ds-feed--child\">\n <span\n class=\"ds-feed__time\"\n [class.ds-feed__time--night]=\"m.night\"\n >\n {{ m.time }}\n </span>\n <span class=\"ds-feed__track\">\n <span\n class=\"ds-feed__fill\"\n [class.ds-feed__fill--night]=\"m.night\"\n [style.width.%]=\"m.barPercent\"\n ></span>\n </span>\n <span class=\"ds-feed__volume\">{{ m.volume }}ml</span>\n <span class=\"ds-feed__flag\" aria-hidden=\"true\"></span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n @if (lowFeedCount() > 0) {\n <div class=\"ds-hint ds-hint--amber\">\n {{ lowFeedCount() }} c\u1EEF \u00EDt h\u01A1n m\u1EE9c g\u1EE3i \u00FD\n @if (s.nutrition) {\n kho\u1EA3ng {{ s.nutrition.expectedPerFeed }}ml m\u1ED7i c\u1EEF v\u1EDBi {{ s.feedCount }} c\u1EEF trong ng\u00E0y\n } @else {\n so v\u1EDBi nh\u1ECBp b\u00FA chung c\u1EE7a ng\u00E0y\n }\n </div>\n }\n }\n </section>\n\n <!-- Ho\u1EA1t \u0111\u1ED9ng -->\n <section aria-label=\"Ho\u1EA1t \u0111\u1ED9ng\">\n <div class=\"ds-head ds-head--purple\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">Ho\u1EA1t \u0111\u1ED9ng</span>\n @if (s.activities.length > 0) {\n <span class=\"ds-head__count\">{{ s.activities.length }}</span>\n }\n </div>\n @if (loadingMedical()) {\n <div class=\"ds-empty\">\u0110ang t\u1EA3i ho\u1EA1t \u0111\u1ED9ng y t\u1EBF\u2026</div>\n } @else if (s.activities.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 ho\u1EA1t \u0111\u1ED9ng n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (act of s.activities; track act.title) {\n <div class=\"ds-card\" [style.border-left-color]=\"act.kindColor\">\n <div class=\"ds-card__top\">\n <span\n class=\"ds-tag\"\n [style.color]=\"act.kindColor\"\n [style.border-color]=\"act.kindColor\"\n >\n <i class=\"fa-solid\" [ngClass]=\"act.kindIcon\" aria-hidden=\"true\"></i>\n {{ act.kindLabel }}\n </span>\n @if (act.place) {\n <span class=\"ds-card__place\">{{ act.place }}</span>\n }\n </div>\n <div class=\"ds-card__title\">{{ act.title }}</div>\n @if (act.detail) {\n <div class=\"ds-card__detail\" [innerHTML]=\"act.detail | safeRichHtml\"></div>\n }\n </div>\n }\n </div>\n }\n </section>\n\n <!-- L\u1ECBch -->\n <section aria-label=\"L\u1ECBch\">\n <div class=\"ds-head ds-head--blue\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">L\u1ECBch</span>\n @if (s.events.length > 0) {\n <span class=\"ds-head__count\">{{ s.events.length }}</span>\n }\n </div>\n @if (s.events.length === 0) {\n <div class=\"ds-empty\">Kh\u00F4ng c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong ng\u00E0y n\u00E0y.</div>\n } @else {\n <div class=\"ds-list\">\n @for (ev of s.events; track ev.time + ev.title) {\n <div class=\"ds-event\">\n <span class=\"ds-event__time\">{{ ev.time }}</span>\n <span class=\"ds-event__body\">\n <span class=\"ds-event__title\">{{ ev.title }}</span>\n @if (ev.place) {\n <span class=\"ds-event__meta\">{{ ev.place }}</span>\n }\n @if (ev.note) {\n <span class=\"ds-event__meta\">{{ ev.note }}</span>\n }\n </span>\n </div>\n }\n </div>\n }\n </section>\n\n <!-- S\u1EAFp t\u1EDBi -->\n <section aria-label=\"S\u1EAFp t\u1EDBi\">\n <div class=\"ds-head ds-head--green\">\n <span class=\"ds-head__bar\"></span>\n <span class=\"ds-head__label\">S\u1EAFp t\u1EDBi</span>\n @if (s.upcoming.length > 0) {\n <span class=\"ds-head__count\">{{ s.upcoming.length }}</span>\n }\n <span class=\"ds-head__spacer\"></span>\n <span class=\"ds-head__action\">{{ lookAheadDays }} ng\u00E0y t\u1EDBi</span>\n </div>\n @if (s.upcoming.length === 0) {\n <div class=\"ds-empty\">Ch\u01B0a c\u00F3 l\u1ECBch h\u1EB9n n\u00E0o trong {{ lookAheadDays }} ng\u00E0y t\u1EDBi.</div>\n } @else {\n <div class=\"ds-list\">\n @for (item of s.upcoming; track item.date + item.title) {\n <div class=\"ds-related ds-related--static\" [style.border-left-color]=\"item.color\">\n <span class=\"ds-related__badge\" [style.color]=\"item.color\">\n <span class=\"ds-related__days\">{{ item.days }}</span>\n <span class=\"ds-related__unit\">ng\u00E0y n\u1EEFa</span>\n </span>\n <span class=\"ds-related__body\">\n <span class=\"ds-related__title\">{{ item.title }}</span>\n <span class=\"ds-related__meta\"> {{ item.weekday }} ng\u00E0y {{ item.displayDate }} </span>\n </span>\n </div>\n }\n </div>\n }\n </section>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f4f6fb;--surface: #ffffff;--border: #dfe5ef;--text: #0b1220;--muted: #5a6a85;--faint: #8c9ab1;--cyan: #0891b2;--cyan-soft: #dff7fd;--cyan-line: #7fdcee;--indigo: #5b5bf0;--indigo-soft: #e9e9ff;--indigo-line: #b4b4fb;--orange: #ef7314;--orange-soft: #fff0e2;--orange-line: #fbc088;--amber: #b45309;--amber-strong: #f59e0b;--amber-soft: #fef6e0;--amber-line: #f6cf7d;--purple: #8b3ff5;--blue: #2563eb;--green: #16a34a;--pink: #e0338c;--r-card: 10px;--r-small: 6px;display:block;background:var(--bg);color:var(--text)}.ds{display:flex;flex-direction:column;gap:18px;padding:14px 14px 32px}.ds-datebar{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:8px 10px}.ds-datebar--weekend{background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-datebar--weekend .ds-datebar__nav{color:var(--indigo)}.ds-datebar--weekend .ds-datebar__nav:disabled{color:var(--faint)}.ds-datebar--weekend .ds-datebar__label{color:var(--indigo)}.ds-datebar__nav{border:none;background:transparent;color:var(--cyan);font-size:15px;padding:6px 10px;cursor:pointer}.ds-datebar__nav:disabled{color:var(--faint);cursor:default}.ds-datebar__center{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:4px 8px;cursor:pointer;border-radius:var(--r-small)}.ds-datebar__label{font-size:15px;font-weight:800;color:var(--cyan);text-align:center;line-height:1.25}.ds-datebar__badge{font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase;color:var(--indigo);background:var(--surface);border:1px solid var(--indigo-line);border-radius:999px;padding:1px 7px}.ds-datebar__input{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:none;cursor:pointer}.ds-reset{align-self:center;margin-top:-12px;display:inline-flex;align-items:center;gap:6px;border:1px solid var(--cyan-line);background:var(--surface);color:var(--cyan);border-radius:999px;padding:6px 14px;font-family:inherit;font-size:12px;font-weight:700;cursor:pointer}.ds-reset i{font-size:12px}.ds-datebar--weekend~.ds-reset{border-color:var(--indigo-line);color:var(--indigo)}.ds-hero{background:var(--cyan-soft);border:1px solid var(--cyan-line);border-radius:var(--r-card);padding:14px;color:var(--text)}.ds-hero__top{display:flex;align-items:flex-end;justify-content:space-between;gap:12px}.ds-hero__label{font-size:11px;font-weight:800;color:var(--cyan)}.ds-hero__total{font-size:38px;font-weight:800;line-height:1.05;margin-top:2px;color:var(--cyan)}.ds-hero__unit{font-size:15px;font-weight:700;margin-left:4px}.ds-hero__delta{display:inline-flex;align-items:center;flex-wrap:wrap;gap:4px;margin-top:6px;font-size:12px;font-weight:700;color:var(--muted)}.ds-hero__delta i{font-size:11px}.ds-hero__delta--up{color:#047857}.ds-hero__delta--down{color:#b45309}.ds-hero__delta--flat{color:var(--muted)}.ds-hero__delta-pct{font-weight:800}.ds-hero__right{text-align:right}.ds-hero__count{font-size:18px;font-weight:800}.ds-hero__avg{font-size:11px;font-weight:600;color:var(--muted)}.ds-hero__stats{display:flex;gap:8px;margin-top:14px}.ds-hero__stat{flex:1;background:var(--surface);border:1px solid var(--cyan-line);border-radius:var(--r-small);padding:8px 10px}.ds-hero__stat-label{display:block;font-size:10px;font-weight:700;color:var(--faint)}.ds-hero__stat-value{display:block;font-size:14px;font-weight:800;margin-top:2px;color:var(--text)}.ds-hero__stat-value--green{color:var(--green)}.ds-hero__stat-value--blue{color:var(--blue)}.ds-hero__stat-value--indigo{color:var(--indigo)}.ds-body-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--green);border-radius:var(--r-card);padding:12px 14px}.ds-body-card__title{font-size:12px;font-weight:800;letter-spacing:.02em;color:var(--muted);margin-bottom:10px}.ds-growth{display:grid;grid-template-columns:1fr 1fr;gap:10px}@media(max-width:420px){.ds-growth{grid-template-columns:1fr}}.ds-growth__card{background:var(--bg);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 11px 11px;min-width:0}.ds-growth__card[data-status=normal]{border-color:#86efac;background:#f0fdf4}.ds-growth__card[data-status=low],.ds-growth__card[data-status=very_low]{border-color:var(--amber-line);background:var(--amber-soft)}.ds-growth__card[data-status=high],.ds-growth__card[data-status=very_high]{border-color:#93c5fd;background:#eff6ff}.ds-growth__head{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-bottom:4px}.ds-growth__label{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:800;color:var(--muted)}.ds-growth__label i{font-size:11px;color:var(--green)}.ds-growth__badge{font-size:10px;font-weight:800;border-radius:999px;padding:2px 7px;white-space:nowrap;background:var(--surface);border:1px solid var(--border);color:var(--muted)}.ds-growth__badge[data-status=normal]{color:var(--green);border-color:#86efac}.ds-growth__badge[data-status=low],.ds-growth__badge[data-status=very_low]{color:var(--amber);border-color:var(--amber-line)}.ds-growth__badge[data-status=high],.ds-growth__badge[data-status=very_high]{color:var(--blue);border-color:#93c5fd}.ds-growth__value{font-size:24px;font-weight:800;line-height:1.1;font-variant-numeric:tabular-nums}.ds-growth__value span{font-size:12px;font-weight:700;margin-left:3px;color:var(--muted)}.ds-growth__compare{display:flex;flex-direction:column;gap:1px;margin-top:6px;font-size:10px;font-weight:600;color:var(--faint)}.ds-growth__scale{margin-top:8px}.ds-growth__scale-labels{display:flex;justify-content:space-between;font-size:9px;font-weight:700;color:var(--faint);margin-bottom:3px}.ds-growth__scale-mid{color:var(--green)}.ds-growth__scale-track{position:relative;display:flex;height:10px;border-radius:5px;overflow:hidden;background:var(--border)}.ds-growth__band{display:block;height:100%}.ds-growth__band--low{width:25%;background:#fcd34d}.ds-growth__band--mid{width:50%;background:#86efac}.ds-growth__band--high{width:25%;background:#93c5fd}.ds-growth__marker{position:absolute;top:50%;width:12px;height:12px;margin-left:-6px;margin-top:-6px;border-radius:50%;background:var(--text);border:2px solid #ffffff;box-sizing:border-box}.ds-growth__marker[data-status=normal]{background:var(--green)}.ds-growth__marker[data-status=low],.ds-growth__marker[data-status=very_low]{background:var(--amber)}.ds-growth__marker[data-status=high],.ds-growth__marker[data-status=very_high]{background:var(--blue)}.ds-growth__scale-legend{display:flex;justify-content:space-between;margin-top:3px;font-size:9px;font-weight:700;color:var(--faint);font-variant-numeric:tabular-nums}.ds-growth__delta{margin-top:7px;font-size:11px;font-weight:700;color:var(--green)}.ds-growth__delta[data-status=low],.ds-growth__delta[data-status=very_low]{color:var(--amber)}.ds-growth__delta[data-status=high],.ds-growth__delta[data-status=very_high]{color:var(--blue)}.ds-growth__delta[data-status=unknown]{color:var(--muted)}.ds-growth__empty{margin-top:8px;font-size:11px;font-weight:600;color:var(--faint);line-height:1.4}.ds-body-card__percent{font-size:18px;font-weight:800;color:var(--green)}.ds-body-card__percent--low{color:var(--amber)}.ds-body-card__meta{font-size:11px;color:var(--faint);margin-top:8px}.ds-target{margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}.ds-target__head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.ds-target__label{font-size:11px;font-weight:800;color:var(--muted)}.ds-target__track{height:10px;background:var(--bg);border-radius:3px;overflow:hidden}.ds-target__fill{display:block;height:100%;background:var(--green)}.ds-target__fill--low{background:var(--amber-strong)}.ds-target__scale{display:flex;justify-content:space-between;gap:10px;font-size:10px;color:var(--faint);margin-top:4px}.ds-target__note{font-size:12px;color:var(--muted);margin-top:6px}.ds-night{background:var(--indigo-soft);border:1px solid var(--indigo-line);border-left:4px solid var(--indigo);border-radius:var(--r-card);padding:10px 12px;font-size:12px;font-weight:700;color:var(--indigo)}.ds-strip{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:10px 6px 8px}.ds-strip__frame{display:flex;align-items:stretch;gap:2px}.ds-strip__nav{flex-shrink:0;align-self:center;width:26px;height:52px;border:1px solid var(--border);border-radius:8px;background:#f8fafc;color:var(--cyan);font-size:12px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0}.ds-strip__nav:disabled{color:var(--faint);cursor:default;opacity:.45}.ds-strip__content{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.ds-strip__chart{display:flex;gap:1px;align-items:flex-end;height:56px;padding:0 0 2px}.ds-strip__bar-col{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:2px;height:100%}.ds-strip__bar-track{flex:1;width:100%;max-width:18px;display:flex;align-items:flex-end;justify-content:center}.ds-strip__bar{width:100%;min-height:3px;border-radius:4px 4px 2px 2px;background:linear-gradient(180deg,#22d3ee,var(--cyan));transition:height .2s ease}.ds-strip__bar--empty{min-height:2px;height:2px!important;background:#e2e8f0;opacity:.7}.ds-strip__bar-col--current .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#34d399,#059669)}.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#a5b4fc,var(--indigo))}.ds-strip__bar-col--current.ds-strip__bar-col--weekend .ds-strip__bar:not(.ds-strip__bar--empty){background:linear-gradient(180deg,#818cf8,#4f46e5)}.ds-strip__bar-label{font-size:8px;font-weight:800;color:var(--muted);line-height:1;font-variant-numeric:tabular-nums}.ds-strip__bar-col--current .ds-strip__bar-label{color:var(--cyan)}.ds-strip__row{display:flex;gap:1px}.ds-strip__cell{flex:1;min-width:0;border:none;background:transparent;padding:2px 0 0;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px;font-family:inherit}.ds-strip__cell:disabled{cursor:default;opacity:.4}.ds-strip__dow{font-size:9px;font-weight:700;color:var(--faint)}.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__day{width:22px;height:22px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:var(--faint);border:1px solid transparent}.ds-strip__day--data{color:var(--text);border-color:var(--border)}.ds-strip__cell--current .ds-strip__dow{color:var(--cyan)}.ds-strip__cell--current .ds-strip__day{background:var(--cyan);border-color:var(--cyan);color:#fff}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__dow{color:var(--indigo)}.ds-strip__cell--current.ds-strip__cell--weekend .ds-strip__day{background:var(--indigo);border-color:var(--indigo)}.ds-strip__dot{width:4px;height:4px;border-radius:2px;background:transparent;display:inline-block}.ds-strip__dot--activity{background:var(--purple)}.ds-strip__dot--event{background:var(--blue)}.ds-strip__legend{display:flex;gap:12px;margin-top:6px;padding-left:2px;font-size:10px;color:var(--muted)}.ds-strip__legend span{display:inline-flex;align-items:center;gap:5px}.ds-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;--tone: var(--cyan)}.ds-head--cyan{--tone: var(--cyan)}.ds-head--orange{--tone: var(--orange)}.ds-head--purple{--tone: var(--purple)}.ds-head--blue{--tone: var(--blue)}.ds-head--green{--tone: var(--green)}.ds-head--pink{--tone: var(--pink)}.ds-head__bar{width:4px;height:16px;border-radius:1px;background:var(--tone)}.ds-head__label{font-size:14px;font-weight:700}.ds-head__count{font-size:11px;font-weight:700;color:#fff;background:var(--tone);border-radius:var(--r-small);padding:2px 7px}.ds-head__spacer{flex:1}.ds-head__action{font-size:11px;font-weight:700;color:var(--tone)}.ds-list{display:flex;flex-direction:column;gap:8px}.ds-empty{background:var(--surface);border:1px dashed var(--border);border-radius:var(--r-card);padding:12px 14px;font-size:12px;color:var(--faint)}.ds-hint{font-size:11px;color:var(--faint)}.ds-hint--amber{color:var(--amber);font-weight:700;margin-top:8px}.ds-tag{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:700;border:1px solid var(--border);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-tag--indigo{color:var(--indigo);background:var(--indigo-soft);border-color:var(--indigo-line)}.ds-tag--amber{color:var(--amber);background:var(--amber-soft);border-color:var(--amber-line)}.ds-tag--cyan{color:var(--cyan);background:var(--cyan-soft);border-color:var(--cyan-line)}.ds-gap{background:var(--surface);border:1px solid var(--orange-line);border-left:4px solid var(--orange);border-radius:var(--r-card);padding:11px 14px}.ds-gap__row{display:flex;align-items:center;gap:10px}.ds-gap__time{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums}.ds-gap__track{position:relative;flex:1;height:20px;display:block}.ds-gap__track:before{content:\"\";position:absolute;top:9px;left:0;right:0;height:3px;background:var(--orange-soft)}.ds-gap__fill{position:absolute;top:9px;left:0;height:3px;background:var(--orange)}.ds-gap__value{position:absolute;top:-1px;left:50%;transform:translate(-50%);font-size:11px;font-weight:800;color:#fff;background:var(--orange);border-radius:var(--r-small);padding:2px 8px;white-space:nowrap}.ds-gap__foot{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:8px}.ds-gap__note{font-size:12px;color:var(--muted)}.ds-gap__volume{font-size:12px;font-weight:700;white-space:nowrap}.ds-feed-periods{display:flex;flex-direction:column;gap:14px}.ds-period__head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:6px;padding:0 2px}.ds-period__label{font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--cyan)}.ds-period__meta{font-size:11px;font-weight:600;color:var(--faint)}.ds-feeds{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);padding:12px;display:flex;flex-direction:column;gap:10px}.ds-feed-block{display:flex;flex-direction:column;gap:5px}.ds-feed{display:grid;grid-template-columns:42px minmax(0,1fr) 52px 22px;align-items:center;column-gap:10px}.ds-feed--child{grid-template-columns:42px minmax(0,1fr) 52px 22px;opacity:.92}.ds-feed__children{display:flex;flex-direction:column;gap:5px;padding-left:10px;border-left:2px solid var(--cyan-line);margin-left:6px}.ds-feed__time{width:42px;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums}.ds-feed__time--night{color:var(--indigo)}.ds-feed__time--low{color:var(--amber)}.ds-feed__time--high{color:var(--green)}.ds-feed--child .ds-feed__time{font-weight:600;color:var(--muted)}.ds-feed--child .ds-feed__time.ds-feed__time--night{color:var(--indigo)}.ds-feed__track{height:12px;background:var(--bg);border-radius:3px;overflow:hidden;display:block;min-width:0}.ds-feed--child .ds-feed__track{height:8px}.ds-feed__fill{display:block;height:100%;background:var(--cyan)}.ds-feed__fill--night{background:var(--indigo)}.ds-feed__fill--under{background:var(--amber-strong)}.ds-feed__fill--low{background:var(--amber-strong);opacity:.75}.ds-feed__fill--high{background:var(--green)}.ds-feed__volume{width:52px;text-align:right;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap}.ds-feed--child .ds-feed__volume{font-weight:600;color:var(--muted)}.ds-feed__flag{width:22px;height:18px;display:inline-flex;align-items:center;justify-content:center}.ds-feed__flag .pi{font-size:12px;font-weight:700}.ds-feed__flag--low{color:var(--amber)}.ds-feed__flag--high{color:var(--green)}.ds-card{background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--purple);border-radius:var(--r-card);padding:11px 13px}.ds-card__top{display:flex;align-items:center;gap:8px;margin-bottom:6px}.ds-card__place{font-size:11px;color:var(--faint)}.ds-card__title{font-size:14px;font-weight:700}.ds-card__detail{font-size:12px;color:var(--muted);margin-top:3px;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;word-break:break-word}.ds-card__detail ::ng-deep p{margin:0;display:inline}.ds-card__detail ::ng-deep p:after{content:\" \"}.ds-card__detail ::ng-deep ul,.ds-card__detail ::ng-deep ol{margin:0;padding-left:1.1em}.ds-card__detail ::ng-deep br{display:none}.ds-event{display:flex;gap:12px;align-items:center;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--blue);border-radius:var(--r-card);padding:11px 13px}.ds-event__time{background:var(--blue);color:#fff;border-radius:var(--r-small);padding:6px 9px;font-size:13px;font-weight:800;white-space:nowrap}.ds-event__title{display:block;font-size:14px;font-weight:700}.ds-event__meta{display:block;font-size:12px;color:var(--muted);margin-top:2px}.ds-related{display:flex;align-items:center;gap:12px;width:100%;text-align:left;background:var(--surface);border:1px solid var(--border);border-left:4px solid var(--pink);border-radius:var(--r-card);padding:10px 12px;cursor:pointer;color:var(--text);font-family:inherit}.ds-related--static{cursor:default}.ds-related__badge{min-width:62px;text-align:center;border:1px solid currentcolor;border-radius:var(--r-small);padding:5px 4px}.ds-related__days{display:block;font-size:15px;font-weight:800}.ds-related__unit{display:block;font-size:10px;font-weight:700}.ds-related__body{flex:1;min-width:0}.ds-related__title{display:block;font-size:13px;font-weight:700}.ds-related__meta{display:block;font-size:11px;color:var(--muted);margin-top:2px}.ds-related__chev{font-size:12px;color:var(--faint)}\n"] }]
21122
21316
  }], ctorParameters: () => [], propDecorators: { logs: [{ type: i0.Input, args: [{ isSignal: true, alias: "logs", required: false }] }], birthDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "birthDate", required: false }] }], initialDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialDate", required: false }] }], feedGroupGapMinutes: [{ type: i0.Input, args: [{ isSignal: true, alias: "feedGroupGapMinutes", required: false }] }], gender: [{ type: i0.Input, args: [{ isSignal: true, alias: "gender", required: false }] }] } });
21123
21317
 
21124
- /**
21125
- * Float menu full-screen — mở từ nút hamburger trên hero.
21126
- * V4: profile card + settings tiles + chức năng list + About + Xoá profile.
21127
- */
21128
- class SettingsMenuComponent {
21129
- constructor() {
21130
- this.appName = input('', ...(ngDevMode ? [{ debugName: "appName" }] : /* istanbul ignore next */ []));
21131
- this.appVersion = input('', ...(ngDevMode ? [{ debugName: "appVersion" }] : /* istanbul ignore next */ []));
21132
- /** Hiển thị entry Explorer (chỉ khi explorer được bật). */
21133
- this.showDocuments = input(false, ...(ngDevMode ? [{ debugName: "showDocuments" }] : /* istanbul ignore next */ []));
21134
- /** Số thông báo chưa đọc — hiển thị badge trên tile Thông báo. */
21135
- this.unreadCount = input(0, ...(ngDevMode ? [{ debugName: "unreadCount" }] : /* istanbul ignore next */ []));
21136
- /** Số món chưa mua — badge trên tile Mua sắm. */
21137
- this.preparingPendingCount = input(0, ...(ngDevMode ? [{ debugName: "preparingPendingCount" }] : /* istanbul ignore next */ []));
21138
- /** Tên bé — profile card + label "Chuẩn bị cho <bé> ra ngoài". */
21139
- this.babyName = input('', ...(ngDevMode ? [{ debugName: "babyName" }] : /* istanbul ignore next */ []));
21140
- /** Chip tuổi (vd. "2t 3w 5n") — hiển thị trên profile card. */
21141
- this.ageLabel = input('', ...(ngDevMode ? [{ debugName: "ageLabel" }] : /* istanbul ignore next */ []));
21142
- /** Cân nặng (vd. "5.2 kg") — hiển thị trên profile card. */
21143
- this.weightLabel = input('', ...(ngDevMode ? [{ debugName: "weightLabel" }] : /* istanbul ignore next */ []));
21144
- /** Ngày sinh hiển thị (vd. "01/04/2026"). */
21145
- this.birthDateLabel = input('', ...(ngDevMode ? [{ debugName: "birthDateLabel" }] : /* istanbul ignore next */ []));
21146
- this.close = output();
21147
- this.select = output();
21148
- this.initials = computed(() => {
21149
- const name = this.babyName().trim();
21150
- if (!name)
21151
- return '?';
21152
- const parts = name.split(/\s+/).filter(Boolean);
21153
- if (parts.length === 1)
21154
- return parts[0].slice(0, 2).toUpperCase();
21155
- return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
21156
- }, ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
21157
- this.profileMeta = computed(() => {
21158
- const bits = [this.ageLabel().trim(), this.weightLabel().trim()].filter(Boolean);
21159
- return bits.join(' · ');
21160
- }, ...(ngDevMode ? [{ debugName: "profileMeta" }] : /* istanbul ignore next */ []));
21161
- this.goingOutLabel = computed(() => {
21162
- const name = this.babyName().trim();
21163
- return name ? `Chuẩn bị cho ${name} ra ngoài` : 'Chuẩn bị đi ra ngoài';
21164
- }, ...(ngDevMode ? [{ debugName: "goingOutLabel" }] : /* istanbul ignore next */ []));
21165
- }
21166
- onSelect(id) {
21167
- this.select.emit(id);
21168
- }
21169
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SettingsMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21170
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SettingsMenuComponent, isStandalone: true, selector: "app-settings-menu", inputs: { appName: { classPropertyName: "appName", publicName: "appName", isSignal: true, isRequired: false, transformFunction: null }, appVersion: { classPropertyName: "appVersion", publicName: "appVersion", isSignal: true, isRequired: false, transformFunction: null }, showDocuments: { classPropertyName: "showDocuments", publicName: "showDocuments", isSignal: true, isRequired: false, transformFunction: null }, unreadCount: { classPropertyName: "unreadCount", publicName: "unreadCount", isSignal: true, isRequired: false, transformFunction: null }, preparingPendingCount: { classPropertyName: "preparingPendingCount", publicName: "preparingPendingCount", isSignal: true, isRequired: false, transformFunction: null }, babyName: { classPropertyName: "babyName", publicName: "babyName", isSignal: true, isRequired: false, transformFunction: null }, ageLabel: { classPropertyName: "ageLabel", publicName: "ageLabel", isSignal: true, isRequired: false, transformFunction: null }, weightLabel: { classPropertyName: "weightLabel", publicName: "weightLabel", isSignal: true, isRequired: false, transformFunction: null }, birthDateLabel: { classPropertyName: "birthDateLabel", publicName: "birthDateLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", select: "select" }, ngImport: i0, template: "<section class=\"sm-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Menu\">\n <header class=\"sm-header\">\n <h1 class=\"sm-title\">{{ appName() }} d\u00E0nh cho <em>{{ babyName() }}</em></h1>\n <span class=\"sm-spacer\"></span>\n <button type=\"button\" class=\"sm-close\" (click)=\"close.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"sm-body\">\n <!-- \u2500\u2500 Profile card \u2500\u2500 -->\n <button\n type=\"button\"\n class=\"sm-profile\"\n (click)=\"onSelect('edit')\"\n aria-label=\"S\u1EEDa profile b\u00E9\"\n >\n <span class=\"sm-profile__avatar\" aria-hidden=\"true\">{{ initials() }}</span>\n <span class=\"sm-profile__body\">\n <span class=\"sm-profile__name\">{{ babyName() || 'Ch\u01B0a c\u00F3 t\u00EAn' }}</span>\n @if (profileMeta()) {\n <span class=\"sm-profile__meta\">{{ profileMeta() }}</span>\n }\n @if (birthDateLabel()) {\n <span class=\"sm-profile__birth\">Sinh {{ birthDateLabel() }}</span>\n }\n </span>\n <span class=\"sm-profile__edit\">\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </span>\n </button>\n\n <!-- \u2500\u2500 Settings tiles \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">C\u00E0i \u0111\u1EB7t</div>\n <div class=\"sm-tiles\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('settings')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--slate\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-gear\"></i>\n </span>\n <span class=\"sm-tile__label\">C\u00E0i \u0111\u1EB7t chung</span>\n <span class=\"sm-tile__hint\">C\u00E0i \u0111\u1EB7t \u1EE9ng d\u1EE5ng</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out-list')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-clipboard-check\"></i>\n </span>\n <span class=\"sm-tile__label\">\u0110i ch\u01A1i</span>\n <span class=\"sm-tile__hint\">Qu\u1EA3n l\u00FD \u0111\u1ED3 \u0111\u1EA1c</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('preparing-purchase')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-cart-shopping\"></i>\n </span>\n <span class=\"sm-tile__label\">Mua s\u1EAFm</span>\n <span class=\"sm-tile__hint\">Checkin \u0111\u1ED3 c\u1EA7n mua</span>\n @if (preparingPendingCount() > 0) {\n <span class=\"sm-tile__badge\">{{ preparingPendingCount() }}</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('notifications')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-bell\"></i>\n </span>\n <span class=\"sm-tile__label\">Th\u00F4ng b\u00E1o</span>\n <span class=\"sm-tile__hint\">M\u1EDBi &amp; \u0111\u00E3 \u0111\u1ECDc</span>\n @if (unreadCount() > 0) {\n <span class=\"sm-tile__badge\">{{ unreadCount() }}</span>\n }\n </button>\n </div>\n </div>\n\n <!-- \u2500\u2500 Features list \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">T\u00EDnh n\u0103ng</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('daily-summary')\"\n >\n <span class=\"sm-item__well sm-item__well--cyan\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-pie\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">T\u1ED5ng qu\u00E1t v\u1EC1 c\u00E1c ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"sm-item__hint\">Th\u00F4ng tin v\u1EC1 c\u1EEF b\u00FA v\u00E0 c\u00E1c ho\u1EA1t \u0111\u1ED9ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('weight')\">\n <span class=\"sm-item__well sm-item__well--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-line\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Th\u1EC3 tr\u1EA1ng</span>\n <span class=\"sm-item__hint\">C\u00E2n n\u1EB7ng, chi\u1EC1u cao, t\u0103ng tr\u01B0\u1EDFng c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('mom')\">\n <span class=\"sm-item__well sm-item__well--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-person-pregnant\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">G\u00F3c c\u1EE7a M\u1EB9</span>\n <span class=\"sm-item__hint\">S\u1EE9c kho\u1EBB &amp; dinh d\u01B0\u1EE1ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out')\"\n >\n <span class=\"sm-item__well sm-item__well--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-plane-departure\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">{{ goingOutLabel() }}</span>\n <span class=\"sm-item__hint\">Checklist \u0111\u1ED3 \u0111i ch\u01A1i</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n @if (showDocuments()) {\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('documents')\"\n >\n <span class=\"sm-item__well sm-item__well--purple\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-folder\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Explorer</span>\n <span class=\"sm-item__hint\">Kho t\u00E0i li\u1EC7u c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </nav>\n </div>\n\n <!-- \u2500\u2500 About \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Th\u00F4ng tin v\u1EC1 \u1EE9ng d\u1EE5ng</div>\n <section class=\"sm-about\" aria-label=\"About\">\n <div class=\"sm-about__icon\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-circle-info\"></i>\n </div>\n <div class=\"sm-about__body\">\n <span class=\"sm-about__name\">{{ appName() }}</span>\n <span class=\"sm-about__version\">Phi\u00EAn b\u1EA3n {{ appVersion() }}</span>\n </div>\n </section>\n </div>\n\n <!-- \u2500\u2500 Danger \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Danger Zone</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item sm-item--danger\"\n role=\"menuitem\"\n (click)=\"onSelect('delete')\"\n >\n <span class=\"sm-item__well sm-item__well--rose\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-trash\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Xo\u00E1 profile</span>\n <span class=\"sm-item__hint\">G\u1EE1 h\u1ED3 s\u01A1 b\u00E9 kh\u1ECFi thi\u1EBFt b\u1ECB n\u00E0y</span>\n </span>\n </button>\n </nav>\n </div>\n </div>\n</section>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f2f3f7;--surface: #ffffff;--border: #e2e5ee;--primary: #5b6cff;--primary-soft: #eef0ff;--accent: #e0338c;--rose: #dc2626;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;--cyan: #0891b2;--cyan-soft: #dff7fd;--green: #16a34a;--green-soft: #e4fbec;--pink: #e0338c;--pink-soft: #ffe8f3;--orange: #ea580c;--orange-soft: #fff0e2;--purple: #7c3aed;--purple-soft: #f2e9ff;--slate: #475569;--slate-soft: #f1f5f9;position:fixed;inset:0;z-index:210;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:sm-slide-in .22s cubic-bezier(.22,1,.36,1)}@keyframes sm-slide-in{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.sm-root{display:flex;flex-direction:column;flex:1;min-height:0}.sm-header{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:10px 14px;padding-top:calc(10px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.sm-title{margin:0;font-size:17px;font-weight:700;color:var(--text)}.sm-spacer{flex:1}.sm-close{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:9px;border:none;background:var(--primary-soft);color:var(--primary);font-size:14px;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,color .15s}.sm-close:hover{filter:brightness(.97)}.sm-body{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:12px 0 calc(24px + env(safe-area-inset-bottom,0px))}.sm-profile{display:flex;align-items:center;gap:12px;width:calc(100% - 24px);margin:0 12px 14px;padding:10px 12px;border:1px solid var(--border);border-radius:16px;background:var(--surface);text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-profile:hover{background:color-mix(in srgb,var(--primary-soft) 40%,var(--surface))}.sm-profile:active{background:var(--primary-soft)}.sm-profile__avatar{flex-shrink:0;width:40px;height:40px;border-radius:14px;background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:800;letter-spacing:.02em}.sm-profile__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-profile__name{font-size:15px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__meta,.sm-profile__birth{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__birth{color:var(--muted-2);font-size:11px}.sm-profile__edit{flex-shrink:0;display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:700;color:var(--primary);background:var(--primary-soft);padding:4px 8px;border-radius:8px}.sm-profile__edit i{font-size:11px}.sm-section{margin-bottom:14px}.sm-section__label{padding:0 18px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.sm-tiles{margin:0 12px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px}.sm-tile{position:relative;display:flex;flex-direction:column;align-items:center;gap:6px;min-height:96px;padding:12px 8px 10px;border:1px solid var(--border);border-radius:14px;background:var(--surface);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-tile:hover{background:color-mix(in srgb,var(--primary-soft) 35%,var(--surface))}.sm-tile:active{background:var(--primary-soft)}.sm-tile__icon{width:38px;height:38px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:16px}.sm-tile__icon--slate{background:var(--slate-soft);color:var(--slate)}.sm-tile__icon--orange{background:var(--orange-soft);color:var(--orange)}.sm-tile__icon--pink{background:var(--pink-soft);color:var(--pink)}.sm-tile__label{font-size:11px;font-weight:700;color:var(--text);text-align:center;line-height:1.2}.sm-tile__hint{font-size:10px;color:var(--muted-2);text-align:center;line-height:1.2}.sm-tile__badge{position:absolute;top:8px;right:8px;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--accent);color:#fff;font-size:10px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}.sm-list{display:flex;flex-direction:column}.sm-list--card{margin:0 12px;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:var(--surface)}.sm-item{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:none;border-bottom:1px solid var(--border);background:transparent;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-item:last-child{border-bottom:none}.sm-item:hover{background:var(--primary-soft)}.sm-item:active{background:color-mix(in srgb,var(--primary-soft) 55%,var(--surface))}.sm-item__well{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;font-size:15px}.sm-item__well--cyan{background:var(--cyan-soft);color:var(--cyan)}.sm-item__well--green{background:var(--green-soft);color:var(--green)}.sm-item__well--pink{background:var(--pink-soft);color:var(--pink)}.sm-item__well--orange{background:var(--orange-soft);color:var(--orange)}.sm-item__well--purple{background:var(--purple-soft);color:var(--purple)}.sm-item__well--rose{background:var(--rose-soft);color:var(--rose)}.sm-item__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-item__label{font-size:14px;font-weight:600;color:var(--text);line-height:1.25}.sm-item__hint{font-size:11px;color:var(--muted-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-item__chev{flex-shrink:0;font-size:13px;color:var(--muted-2)}.sm-item--danger:hover{background:var(--rose-soft)}.sm-item--danger:active{background:color-mix(in srgb,var(--rose-soft) 55%,var(--surface))}.sm-item--danger .sm-item__label{color:var(--rose)}.sm-about{display:flex;align-items:center;gap:12px;margin:0 12px;padding:12px 14px;border-radius:14px;background:var(--surface);border:1px solid var(--border)}.sm-about__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;background:var(--primary-soft);color:var(--primary);font-size:16px;display:flex;align-items:center;justify-content:center}.sm-about__body{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.sm-about__name{font-size:14px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-about__version{font-size:11px;color:var(--muted)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21171
- }
21172
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SettingsMenuComponent, decorators: [{
21173
- type: Component,
21174
- args: [{ selector: 'app-settings-menu', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"sm-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Menu\">\n <header class=\"sm-header\">\n <h1 class=\"sm-title\">{{ appName() }} d\u00E0nh cho <em>{{ babyName() }}</em></h1>\n <span class=\"sm-spacer\"></span>\n <button type=\"button\" class=\"sm-close\" (click)=\"close.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"sm-body\">\n <!-- \u2500\u2500 Profile card \u2500\u2500 -->\n <button\n type=\"button\"\n class=\"sm-profile\"\n (click)=\"onSelect('edit')\"\n aria-label=\"S\u1EEDa profile b\u00E9\"\n >\n <span class=\"sm-profile__avatar\" aria-hidden=\"true\">{{ initials() }}</span>\n <span class=\"sm-profile__body\">\n <span class=\"sm-profile__name\">{{ babyName() || 'Ch\u01B0a c\u00F3 t\u00EAn' }}</span>\n @if (profileMeta()) {\n <span class=\"sm-profile__meta\">{{ profileMeta() }}</span>\n }\n @if (birthDateLabel()) {\n <span class=\"sm-profile__birth\">Sinh {{ birthDateLabel() }}</span>\n }\n </span>\n <span class=\"sm-profile__edit\">\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </span>\n </button>\n\n <!-- \u2500\u2500 Settings tiles \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">C\u00E0i \u0111\u1EB7t</div>\n <div class=\"sm-tiles\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('settings')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--slate\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-gear\"></i>\n </span>\n <span class=\"sm-tile__label\">C\u00E0i \u0111\u1EB7t chung</span>\n <span class=\"sm-tile__hint\">C\u00E0i \u0111\u1EB7t \u1EE9ng d\u1EE5ng</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out-list')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-clipboard-check\"></i>\n </span>\n <span class=\"sm-tile__label\">\u0110i ch\u01A1i</span>\n <span class=\"sm-tile__hint\">Qu\u1EA3n l\u00FD \u0111\u1ED3 \u0111\u1EA1c</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('preparing-purchase')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-cart-shopping\"></i>\n </span>\n <span class=\"sm-tile__label\">Mua s\u1EAFm</span>\n <span class=\"sm-tile__hint\">Checkin \u0111\u1ED3 c\u1EA7n mua</span>\n @if (preparingPendingCount() > 0) {\n <span class=\"sm-tile__badge\">{{ preparingPendingCount() }}</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('notifications')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-bell\"></i>\n </span>\n <span class=\"sm-tile__label\">Th\u00F4ng b\u00E1o</span>\n <span class=\"sm-tile__hint\">M\u1EDBi &amp; \u0111\u00E3 \u0111\u1ECDc</span>\n @if (unreadCount() > 0) {\n <span class=\"sm-tile__badge\">{{ unreadCount() }}</span>\n }\n </button>\n </div>\n </div>\n\n <!-- \u2500\u2500 Features list \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">T\u00EDnh n\u0103ng</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('daily-summary')\"\n >\n <span class=\"sm-item__well sm-item__well--cyan\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-pie\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">T\u1ED5ng qu\u00E1t v\u1EC1 c\u00E1c ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"sm-item__hint\">Th\u00F4ng tin v\u1EC1 c\u1EEF b\u00FA v\u00E0 c\u00E1c ho\u1EA1t \u0111\u1ED9ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('weight')\">\n <span class=\"sm-item__well sm-item__well--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-line\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Th\u1EC3 tr\u1EA1ng</span>\n <span class=\"sm-item__hint\">C\u00E2n n\u1EB7ng, chi\u1EC1u cao, t\u0103ng tr\u01B0\u1EDFng c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('mom')\">\n <span class=\"sm-item__well sm-item__well--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-person-pregnant\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">G\u00F3c c\u1EE7a M\u1EB9</span>\n <span class=\"sm-item__hint\">S\u1EE9c kho\u1EBB &amp; dinh d\u01B0\u1EE1ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out')\"\n >\n <span class=\"sm-item__well sm-item__well--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-plane-departure\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">{{ goingOutLabel() }}</span>\n <span class=\"sm-item__hint\">Checklist \u0111\u1ED3 \u0111i ch\u01A1i</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n @if (showDocuments()) {\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('documents')\"\n >\n <span class=\"sm-item__well sm-item__well--purple\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-folder\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Explorer</span>\n <span class=\"sm-item__hint\">Kho t\u00E0i li\u1EC7u c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </nav>\n </div>\n\n <!-- \u2500\u2500 About \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Th\u00F4ng tin v\u1EC1 \u1EE9ng d\u1EE5ng</div>\n <section class=\"sm-about\" aria-label=\"About\">\n <div class=\"sm-about__icon\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-circle-info\"></i>\n </div>\n <div class=\"sm-about__body\">\n <span class=\"sm-about__name\">{{ appName() }}</span>\n <span class=\"sm-about__version\">Phi\u00EAn b\u1EA3n {{ appVersion() }}</span>\n </div>\n </section>\n </div>\n\n <!-- \u2500\u2500 Danger \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Danger Zone</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item sm-item--danger\"\n role=\"menuitem\"\n (click)=\"onSelect('delete')\"\n >\n <span class=\"sm-item__well sm-item__well--rose\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-trash\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Xo\u00E1 profile</span>\n <span class=\"sm-item__hint\">G\u1EE1 h\u1ED3 s\u01A1 b\u00E9 kh\u1ECFi thi\u1EBFt b\u1ECB n\u00E0y</span>\n </span>\n </button>\n </nav>\n </div>\n </div>\n</section>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f2f3f7;--surface: #ffffff;--border: #e2e5ee;--primary: #5b6cff;--primary-soft: #eef0ff;--accent: #e0338c;--rose: #dc2626;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;--cyan: #0891b2;--cyan-soft: #dff7fd;--green: #16a34a;--green-soft: #e4fbec;--pink: #e0338c;--pink-soft: #ffe8f3;--orange: #ea580c;--orange-soft: #fff0e2;--purple: #7c3aed;--purple-soft: #f2e9ff;--slate: #475569;--slate-soft: #f1f5f9;position:fixed;inset:0;z-index:210;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:sm-slide-in .22s cubic-bezier(.22,1,.36,1)}@keyframes sm-slide-in{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.sm-root{display:flex;flex-direction:column;flex:1;min-height:0}.sm-header{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:10px 14px;padding-top:calc(10px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.sm-title{margin:0;font-size:17px;font-weight:700;color:var(--text)}.sm-spacer{flex:1}.sm-close{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:9px;border:none;background:var(--primary-soft);color:var(--primary);font-size:14px;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,color .15s}.sm-close:hover{filter:brightness(.97)}.sm-body{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:12px 0 calc(24px + env(safe-area-inset-bottom,0px))}.sm-profile{display:flex;align-items:center;gap:12px;width:calc(100% - 24px);margin:0 12px 14px;padding:10px 12px;border:1px solid var(--border);border-radius:16px;background:var(--surface);text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-profile:hover{background:color-mix(in srgb,var(--primary-soft) 40%,var(--surface))}.sm-profile:active{background:var(--primary-soft)}.sm-profile__avatar{flex-shrink:0;width:40px;height:40px;border-radius:14px;background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:800;letter-spacing:.02em}.sm-profile__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-profile__name{font-size:15px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__meta,.sm-profile__birth{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__birth{color:var(--muted-2);font-size:11px}.sm-profile__edit{flex-shrink:0;display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:700;color:var(--primary);background:var(--primary-soft);padding:4px 8px;border-radius:8px}.sm-profile__edit i{font-size:11px}.sm-section{margin-bottom:14px}.sm-section__label{padding:0 18px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.sm-tiles{margin:0 12px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px}.sm-tile{position:relative;display:flex;flex-direction:column;align-items:center;gap:6px;min-height:96px;padding:12px 8px 10px;border:1px solid var(--border);border-radius:14px;background:var(--surface);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-tile:hover{background:color-mix(in srgb,var(--primary-soft) 35%,var(--surface))}.sm-tile:active{background:var(--primary-soft)}.sm-tile__icon{width:38px;height:38px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:16px}.sm-tile__icon--slate{background:var(--slate-soft);color:var(--slate)}.sm-tile__icon--orange{background:var(--orange-soft);color:var(--orange)}.sm-tile__icon--pink{background:var(--pink-soft);color:var(--pink)}.sm-tile__label{font-size:11px;font-weight:700;color:var(--text);text-align:center;line-height:1.2}.sm-tile__hint{font-size:10px;color:var(--muted-2);text-align:center;line-height:1.2}.sm-tile__badge{position:absolute;top:8px;right:8px;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--accent);color:#fff;font-size:10px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}.sm-list{display:flex;flex-direction:column}.sm-list--card{margin:0 12px;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:var(--surface)}.sm-item{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:none;border-bottom:1px solid var(--border);background:transparent;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-item:last-child{border-bottom:none}.sm-item:hover{background:var(--primary-soft)}.sm-item:active{background:color-mix(in srgb,var(--primary-soft) 55%,var(--surface))}.sm-item__well{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;font-size:15px}.sm-item__well--cyan{background:var(--cyan-soft);color:var(--cyan)}.sm-item__well--green{background:var(--green-soft);color:var(--green)}.sm-item__well--pink{background:var(--pink-soft);color:var(--pink)}.sm-item__well--orange{background:var(--orange-soft);color:var(--orange)}.sm-item__well--purple{background:var(--purple-soft);color:var(--purple)}.sm-item__well--rose{background:var(--rose-soft);color:var(--rose)}.sm-item__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-item__label{font-size:14px;font-weight:600;color:var(--text);line-height:1.25}.sm-item__hint{font-size:11px;color:var(--muted-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-item__chev{flex-shrink:0;font-size:13px;color:var(--muted-2)}.sm-item--danger:hover{background:var(--rose-soft)}.sm-item--danger:active{background:color-mix(in srgb,var(--rose-soft) 55%,var(--surface))}.sm-item--danger .sm-item__label{color:var(--rose)}.sm-about{display:flex;align-items:center;gap:12px;margin:0 12px;padding:12px 14px;border-radius:14px;background:var(--surface);border:1px solid var(--border)}.sm-about__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;background:var(--primary-soft);color:var(--primary);font-size:16px;display:flex;align-items:center;justify-content:center}.sm-about__body{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.sm-about__name{font-size:14px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-about__version{font-size:11px;color:var(--muted)}\n"] }]
21175
- }], propDecorators: { appName: [{ type: i0.Input, args: [{ isSignal: true, alias: "appName", required: false }] }], appVersion: [{ type: i0.Input, args: [{ isSignal: true, alias: "appVersion", required: false }] }], showDocuments: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDocuments", required: false }] }], unreadCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "unreadCount", required: false }] }], preparingPendingCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "preparingPendingCount", required: false }] }], babyName: [{ type: i0.Input, args: [{ isSignal: true, alias: "babyName", required: false }] }], ageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ageLabel", required: false }] }], weightLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "weightLabel", required: false }] }], birthDateLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "birthDateLabel", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], select: [{ type: i0.Output, args: ["select"] }] } });
21176
-
21177
21318
  const STORAGE_KEY = 'going-out-checklist::v2';
21178
21319
  const DEFAULT_SETTINGS = { allowEdit: true, allowAdd: true, allowPartial: true };
21179
21320
  function toTrip(items) {
@@ -21232,6 +21373,16 @@ class GoingOutChecklistService {
21232
21373
  this.notNeededCount = computed(() => this.trip().filter((i) => i.notNeeded).length, ...(ngDevMode ? [{ debugName: "notNeededCount" }] : /* istanbul ignore next */ []));
21233
21374
  this.total = computed(() => this.trip().length, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
21234
21375
  this.allDone = computed(() => this.trip().length > 0 && this.trip().every((i) => isTripSatisfied(i)), ...(ngDevMode ? [{ debugName: "allDone" }] : /* istanbul ignore next */ []));
21376
+ /** Món cần mang lần này (không tính notNeeded). */
21377
+ this.activeTripCount = computed(() => this.trip().filter((i) => !i.notNeeded).length, ...(ngDevMode ? [{ debugName: "activeTripCount" }] : /* istanbul ignore next */ []));
21378
+ /** Đã gói xong (status done, vẫn đang mang). */
21379
+ this.packedCount = computed(() => this.trip().filter((i) => !i.notNeeded && i.status === 'done').length, ...(ngDevMode ? [{ debugName: "packedCount" }] : /* istanbul ignore next */ []));
21380
+ this.progressPercent = computed(() => {
21381
+ const total = this.activeTripCount();
21382
+ if (!total)
21383
+ return 0;
21384
+ return Math.round((this.packedCount() / total) * 100);
21385
+ }, ...(ngDevMode ? [{ debugName: "progressPercent" }] : /* istanbul ignore next */ []));
21235
21386
  this.allowEdit = computed(() => this.settings().allowEdit, ...(ngDevMode ? [{ debugName: "allowEdit" }] : /* istanbul ignore next */ []));
21236
21387
  this.allowAdd = computed(() => this.settings().allowAdd, ...(ngDevMode ? [{ debugName: "allowAdd" }] : /* istanbul ignore next */ []));
21237
21388
  this.allowPartial = computed(() => this.settings().allowPartial, ...(ngDevMode ? [{ debugName: "allowPartial" }] : /* istanbul ignore next */ []));
@@ -21272,7 +21423,8 @@ class GoingOutChecklistService {
21272
21423
  title: withDetails ? patch.title.trim() || it.title : it.title,
21273
21424
  desc: withDetails ? patch.desc.trim() : it.desc,
21274
21425
  note: patch.note.trim(),
21275
- status: patch.status,
21426
+ notNeeded: patch.notNeeded,
21427
+ status: patch.notNeeded ? 'none' : patch.status,
21276
21428
  }
21277
21429
  : it));
21278
21430
  }
@@ -21280,6 +21432,35 @@ class GoingOutChecklistService {
21280
21432
  removeFromTrip(id) {
21281
21433
  this.trip.update((rows) => rows.filter((it) => it.id !== id));
21282
21434
  }
21435
+ /** Đổi thứ tự checklist chuyến — chỉ localStorage, không ghi Sheet. */
21436
+ moveTripItem(index, delta) {
21437
+ if (!delta)
21438
+ return;
21439
+ this.trip.update((rows) => {
21440
+ const to = index + delta;
21441
+ if (to < 0 || to >= rows.length)
21442
+ return rows;
21443
+ const next = [...rows];
21444
+ const tmp = next[index];
21445
+ next[index] = next[to];
21446
+ next[to] = tmp;
21447
+ return next;
21448
+ });
21449
+ }
21450
+ reorderTrip(draggedId, targetId) {
21451
+ if (draggedId === targetId)
21452
+ return;
21453
+ this.trip.update((rows) => {
21454
+ const from = rows.findIndex((r) => r.id === draggedId);
21455
+ const to = rows.findIndex((r) => r.id === targetId);
21456
+ if (from < 0 || to < 0)
21457
+ return rows;
21458
+ const next = [...rows];
21459
+ const [moved] = next.splice(from, 1);
21460
+ next.splice(to, 0, moved);
21461
+ return next;
21462
+ });
21463
+ }
21283
21464
  /** Khôi phục checklist theo danh sách mẫu: tick, ghi chú, món đã ẩn / thêm tạm. */
21284
21465
  resetTrip() {
21285
21466
  this.trip.set(toTrip(this.items()));
@@ -21305,18 +21486,21 @@ class GoingOutChecklistService {
21305
21486
  next: () => {
21306
21487
  this.sheetSyncError.set('');
21307
21488
  this.bootstrap.invalidate('goingOutItems');
21308
- this.bootstrap.invalidate('preparingProducts');
21309
21489
  this.reloadFromSheet().subscribe();
21310
21490
  },
21311
21491
  error: () => this.rollbackMasterAdd(item.id),
21312
21492
  });
21313
21493
  }
21314
- addChecklistItem(title, desc, note, status) {
21494
+ addChecklistItem(title, desc, note, status, notNeeded = false) {
21315
21495
  const t = title.trim();
21316
21496
  if (!t)
21317
21497
  return;
21318
21498
  const item = { id: genId(), title: t, desc: desc.trim() };
21319
- this.trip.update((rows) => [...rows, { ...item, note: note.trim(), status, notNeeded: false }]);
21499
+ const tripStatus = notNeeded ? 'none' : status;
21500
+ this.trip.update((rows) => [
21501
+ ...rows,
21502
+ { ...item, note: note.trim(), status: tripStatus, notNeeded },
21503
+ ]);
21320
21504
  }
21321
21505
  updateMaster(id, title, desc) {
21322
21506
  const t = title.trim();
@@ -21333,7 +21517,6 @@ class GoingOutChecklistService {
21333
21517
  next: () => {
21334
21518
  this.sheetSyncError.set('');
21335
21519
  this.bootstrap.invalidate('goingOutItems');
21336
- this.bootstrap.invalidate('preparingProducts');
21337
21520
  this.reloadFromSheet().subscribe();
21338
21521
  },
21339
21522
  error: () => {
@@ -21357,7 +21540,6 @@ class GoingOutChecklistService {
21357
21540
  next: () => {
21358
21541
  this.sheetSyncError.set('');
21359
21542
  this.bootstrap.invalidate('goingOutItems');
21360
- this.bootstrap.invalidate('preparingProducts');
21361
21543
  this.reloadFromSheet().subscribe();
21362
21544
  },
21363
21545
  error: () => {
@@ -21509,6 +21691,71 @@ function normalizeTrip(item) {
21509
21691
  return { ...item, notNeeded: Boolean(item.notNeeded) };
21510
21692
  }
21511
21693
 
21694
+ /**
21695
+ * Float menu full-screen — mở từ nút hamburger trên hero.
21696
+ * V4: profile card + settings tiles + chức năng list + About + Xoá profile.
21697
+ */
21698
+ class SettingsMenuComponent {
21699
+ constructor() {
21700
+ this.goingOut = inject(GoingOutChecklistService);
21701
+ this.appName = input('', ...(ngDevMode ? [{ debugName: "appName" }] : /* istanbul ignore next */ []));
21702
+ this.appVersion = input('', ...(ngDevMode ? [{ debugName: "appVersion" }] : /* istanbul ignore next */ []));
21703
+ /** Hiển thị entry Explorer (chỉ khi explorer được bật). */
21704
+ this.showDocuments = input(false, ...(ngDevMode ? [{ debugName: "showDocuments" }] : /* istanbul ignore next */ []));
21705
+ /** Số thông báo chưa đọc — hiển thị badge trên tile Thông báo. */
21706
+ this.unreadCount = input(0, ...(ngDevMode ? [{ debugName: "unreadCount" }] : /* istanbul ignore next */ []));
21707
+ /** Số món chưa mua — badge trên tile Mua sắm. */
21708
+ this.preparingPendingCount = input(0, ...(ngDevMode ? [{ debugName: "preparingPendingCount" }] : /* istanbul ignore next */ []));
21709
+ /** Tên bé — profile card + label "Chuẩn bị cho <bé> ra ngoài". */
21710
+ this.babyName = input('', ...(ngDevMode ? [{ debugName: "babyName" }] : /* istanbul ignore next */ []));
21711
+ /** Chip tuổi (vd. "2t 3w 5n") — hiển thị trên profile card. */
21712
+ this.ageLabel = input('', ...(ngDevMode ? [{ debugName: "ageLabel" }] : /* istanbul ignore next */ []));
21713
+ /** Cân nặng (vd. "5.2 kg") — hiển thị trên profile card. */
21714
+ this.weightLabel = input('', ...(ngDevMode ? [{ debugName: "weightLabel" }] : /* istanbul ignore next */ []));
21715
+ /** Ngày sinh hiển thị (vd. "01/04/2026"). */
21716
+ this.birthDateLabel = input('', ...(ngDevMode ? [{ debugName: "birthDateLabel" }] : /* istanbul ignore next */ []));
21717
+ this.close = output();
21718
+ this.select = output();
21719
+ this.initials = computed(() => {
21720
+ const name = this.babyName().trim();
21721
+ if (!name)
21722
+ return '?';
21723
+ const parts = name.split(/\s+/).filter(Boolean);
21724
+ if (parts.length === 1)
21725
+ return parts[0].slice(0, 2).toUpperCase();
21726
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
21727
+ }, ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
21728
+ this.profileMeta = computed(() => {
21729
+ const bits = [this.ageLabel().trim(), this.weightLabel().trim()].filter(Boolean);
21730
+ return bits.join(' · ');
21731
+ }, ...(ngDevMode ? [{ debugName: "profileMeta" }] : /* istanbul ignore next */ []));
21732
+ this.goingOutLabel = computed(() => {
21733
+ const name = this.babyName().trim();
21734
+ return name ? `Chuẩn bị cho ${name} ra ngoài` : 'Chuẩn bị đi ra ngoài';
21735
+ }, ...(ngDevMode ? [{ debugName: "goingOutLabel" }] : /* istanbul ignore next */ []));
21736
+ this.goingOutHint = computed(() => {
21737
+ const trip = this.goingOut.trip();
21738
+ if (!trip.length)
21739
+ return 'Checklist đồ đi chơi';
21740
+ const active = trip.filter((i) => !i.notNeeded);
21741
+ const packed = active.filter((i) => i.status === 'done').length;
21742
+ const total = active.length;
21743
+ if (!total)
21744
+ return 'Không mang món nào lần này';
21745
+ return `${packed}/${total} sẵn sàng · ${this.goingOut.progressPercent()}%`;
21746
+ }, ...(ngDevMode ? [{ debugName: "goingOutHint" }] : /* istanbul ignore next */ []));
21747
+ }
21748
+ onSelect(id) {
21749
+ this.select.emit(id);
21750
+ }
21751
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SettingsMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21752
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SettingsMenuComponent, isStandalone: true, selector: "app-settings-menu", inputs: { appName: { classPropertyName: "appName", publicName: "appName", isSignal: true, isRequired: false, transformFunction: null }, appVersion: { classPropertyName: "appVersion", publicName: "appVersion", isSignal: true, isRequired: false, transformFunction: null }, showDocuments: { classPropertyName: "showDocuments", publicName: "showDocuments", isSignal: true, isRequired: false, transformFunction: null }, unreadCount: { classPropertyName: "unreadCount", publicName: "unreadCount", isSignal: true, isRequired: false, transformFunction: null }, preparingPendingCount: { classPropertyName: "preparingPendingCount", publicName: "preparingPendingCount", isSignal: true, isRequired: false, transformFunction: null }, babyName: { classPropertyName: "babyName", publicName: "babyName", isSignal: true, isRequired: false, transformFunction: null }, ageLabel: { classPropertyName: "ageLabel", publicName: "ageLabel", isSignal: true, isRequired: false, transformFunction: null }, weightLabel: { classPropertyName: "weightLabel", publicName: "weightLabel", isSignal: true, isRequired: false, transformFunction: null }, birthDateLabel: { classPropertyName: "birthDateLabel", publicName: "birthDateLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", select: "select" }, ngImport: i0, template: "<section class=\"sm-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Menu\">\n <header class=\"sm-header\">\n <h1 class=\"sm-title\">{{ appName() }} d\u00E0nh cho <em>{{ babyName() }}</em></h1>\n <span class=\"sm-spacer\"></span>\n <button type=\"button\" class=\"sm-close\" (click)=\"close.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"sm-body\">\n <!-- \u2500\u2500 Profile card \u2500\u2500 -->\n <button\n type=\"button\"\n class=\"sm-profile\"\n (click)=\"onSelect('edit')\"\n aria-label=\"S\u1EEDa profile b\u00E9\"\n >\n <span class=\"sm-profile__avatar\" aria-hidden=\"true\">{{ initials() }}</span>\n <span class=\"sm-profile__body\">\n <span class=\"sm-profile__name\">{{ babyName() || 'Ch\u01B0a c\u00F3 t\u00EAn' }}</span>\n @if (profileMeta()) {\n <span class=\"sm-profile__meta\">{{ profileMeta() }}</span>\n }\n @if (birthDateLabel()) {\n <span class=\"sm-profile__birth\">Sinh {{ birthDateLabel() }}</span>\n }\n </span>\n <span class=\"sm-profile__edit\">\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </span>\n </button>\n\n <!-- \u2500\u2500 Settings tiles \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">C\u00E0i \u0111\u1EB7t</div>\n <div class=\"sm-tiles\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('settings')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--slate\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-gear\"></i>\n </span>\n <span class=\"sm-tile__label\">C\u00E0i \u0111\u1EB7t chung</span>\n <span class=\"sm-tile__hint\">C\u00E0i \u0111\u1EB7t \u1EE9ng d\u1EE5ng</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out-list')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-clipboard-check\"></i>\n </span>\n <span class=\"sm-tile__label\">\u0110i ch\u01A1i</span>\n <span class=\"sm-tile__hint\">Qu\u1EA3n l\u00FD \u0111\u1ED3 \u0111\u1EA1c</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('preparing-purchase')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-cart-shopping\"></i>\n </span>\n <span class=\"sm-tile__label\">Mua s\u1EAFm</span>\n <span class=\"sm-tile__hint\">Checkin \u0111\u1ED3 c\u1EA7n mua</span>\n @if (preparingPendingCount() > 0) {\n <span class=\"sm-tile__badge\">{{ preparingPendingCount() }}</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('notifications')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-bell\"></i>\n </span>\n <span class=\"sm-tile__label\">Th\u00F4ng b\u00E1o</span>\n <span class=\"sm-tile__hint\">M\u1EDBi &amp; \u0111\u00E3 \u0111\u1ECDc</span>\n @if (unreadCount() > 0) {\n <span class=\"sm-tile__badge\">{{ unreadCount() }}</span>\n }\n </button>\n </div>\n </div>\n\n <!-- \u2500\u2500 Features list \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">T\u00EDnh n\u0103ng</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('daily-summary')\"\n >\n <span class=\"sm-item__well sm-item__well--cyan\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-pie\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">T\u1ED5ng qu\u00E1t v\u1EC1 c\u00E1c ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"sm-item__hint\">Th\u00F4ng tin v\u1EC1 c\u1EEF b\u00FA v\u00E0 c\u00E1c ho\u1EA1t \u0111\u1ED9ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('weight')\">\n <span class=\"sm-item__well sm-item__well--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-line\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Th\u1EC3 tr\u1EA1ng</span>\n <span class=\"sm-item__hint\">C\u00E2n n\u1EB7ng, chi\u1EC1u cao, t\u0103ng tr\u01B0\u1EDFng c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('mom')\">\n <span class=\"sm-item__well sm-item__well--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-person-pregnant\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">G\u00F3c c\u1EE7a M\u1EB9</span>\n <span class=\"sm-item__hint\">S\u1EE9c kho\u1EBB &amp; dinh d\u01B0\u1EE1ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out')\"\n >\n <span class=\"sm-item__well sm-item__well--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-plane-departure\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">{{ goingOutLabel() }}</span>\n <span class=\"sm-item__hint\">{{ goingOutHint() }}</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n @if (showDocuments()) {\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('documents')\"\n >\n <span class=\"sm-item__well sm-item__well--purple\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-folder\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Explorer</span>\n <span class=\"sm-item__hint\">Kho t\u00E0i li\u1EC7u c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </nav>\n </div>\n\n <!-- \u2500\u2500 About \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Th\u00F4ng tin v\u1EC1 \u1EE9ng d\u1EE5ng</div>\n <section class=\"sm-about\" aria-label=\"About\">\n <div class=\"sm-about__icon\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-circle-info\"></i>\n </div>\n <div class=\"sm-about__body\">\n <span class=\"sm-about__name\">{{ appName() }}</span>\n <span class=\"sm-about__version\">Phi\u00EAn b\u1EA3n {{ appVersion() }}</span>\n </div>\n </section>\n </div>\n\n <!-- \u2500\u2500 Danger \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Danger Zone</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item sm-item--danger\"\n role=\"menuitem\"\n (click)=\"onSelect('delete')\"\n >\n <span class=\"sm-item__well sm-item__well--rose\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-trash\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Xo\u00E1 profile</span>\n <span class=\"sm-item__hint\">G\u1EE1 h\u1ED3 s\u01A1 b\u00E9 kh\u1ECFi thi\u1EBFt b\u1ECB n\u00E0y</span>\n </span>\n </button>\n </nav>\n </div>\n </div>\n</section>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f2f3f7;--surface: #ffffff;--border: #e2e5ee;--primary: #5b6cff;--primary-soft: #eef0ff;--accent: #e0338c;--rose: #dc2626;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;--cyan: #0891b2;--cyan-soft: #dff7fd;--green: #16a34a;--green-soft: #e4fbec;--pink: #e0338c;--pink-soft: #ffe8f3;--orange: #ea580c;--orange-soft: #fff0e2;--purple: #7c3aed;--purple-soft: #f2e9ff;--slate: #475569;--slate-soft: #f1f5f9;position:fixed;inset:0;z-index:210;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:sm-slide-in .22s cubic-bezier(.22,1,.36,1)}@keyframes sm-slide-in{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.sm-root{display:flex;flex-direction:column;flex:1;min-height:0}.sm-header{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:10px 14px;padding-top:calc(10px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.sm-title{margin:0;font-size:17px;font-weight:700;color:var(--text)}.sm-spacer{flex:1}.sm-close{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:9px;border:none;background:var(--primary-soft);color:var(--primary);font-size:14px;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,color .15s}.sm-close:hover{filter:brightness(.97)}.sm-body{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:12px 0 calc(24px + env(safe-area-inset-bottom,0px))}.sm-profile{display:flex;align-items:center;gap:12px;width:calc(100% - 24px);margin:0 12px 14px;padding:10px 12px;border:1px solid var(--border);border-radius:16px;background:var(--surface);text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-profile:hover{background:color-mix(in srgb,var(--primary-soft) 40%,var(--surface))}.sm-profile:active{background:var(--primary-soft)}.sm-profile__avatar{flex-shrink:0;width:40px;height:40px;border-radius:14px;background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:800;letter-spacing:.02em}.sm-profile__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-profile__name{font-size:15px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__meta,.sm-profile__birth{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__birth{color:var(--muted-2);font-size:11px}.sm-profile__edit{flex-shrink:0;display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:700;color:var(--primary);background:var(--primary-soft);padding:4px 8px;border-radius:8px}.sm-profile__edit i{font-size:11px}.sm-section{margin-bottom:14px}.sm-section__label{padding:0 18px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.sm-tiles{margin:0 12px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px}.sm-tile{position:relative;display:flex;flex-direction:column;align-items:center;gap:6px;min-height:96px;padding:12px 8px 10px;border:1px solid var(--border);border-radius:14px;background:var(--surface);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-tile:hover{background:color-mix(in srgb,var(--primary-soft) 35%,var(--surface))}.sm-tile:active{background:var(--primary-soft)}.sm-tile__icon{width:38px;height:38px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:16px}.sm-tile__icon--slate{background:var(--slate-soft);color:var(--slate)}.sm-tile__icon--orange{background:var(--orange-soft);color:var(--orange)}.sm-tile__icon--pink{background:var(--pink-soft);color:var(--pink)}.sm-tile__label{font-size:11px;font-weight:700;color:var(--text);text-align:center;line-height:1.2}.sm-tile__hint{font-size:10px;color:var(--muted-2);text-align:center;line-height:1.2}.sm-tile__badge{position:absolute;top:8px;right:8px;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--accent);color:#fff;font-size:10px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}.sm-list{display:flex;flex-direction:column}.sm-list--card{margin:0 12px;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:var(--surface)}.sm-item{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:none;border-bottom:1px solid var(--border);background:transparent;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-item:last-child{border-bottom:none}.sm-item:hover{background:var(--primary-soft)}.sm-item:active{background:color-mix(in srgb,var(--primary-soft) 55%,var(--surface))}.sm-item__well{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;font-size:15px}.sm-item__well--cyan{background:var(--cyan-soft);color:var(--cyan)}.sm-item__well--green{background:var(--green-soft);color:var(--green)}.sm-item__well--pink{background:var(--pink-soft);color:var(--pink)}.sm-item__well--orange{background:var(--orange-soft);color:var(--orange)}.sm-item__well--purple{background:var(--purple-soft);color:var(--purple)}.sm-item__well--rose{background:var(--rose-soft);color:var(--rose)}.sm-item__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-item__label{font-size:14px;font-weight:600;color:var(--text);line-height:1.25}.sm-item__hint{font-size:11px;color:var(--muted-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-item__chev{flex-shrink:0;font-size:13px;color:var(--muted-2)}.sm-item--danger:hover{background:var(--rose-soft)}.sm-item--danger:active{background:color-mix(in srgb,var(--rose-soft) 55%,var(--surface))}.sm-item--danger .sm-item__label{color:var(--rose)}.sm-about{display:flex;align-items:center;gap:12px;margin:0 12px;padding:12px 14px;border-radius:14px;background:var(--surface);border:1px solid var(--border)}.sm-about__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;background:var(--primary-soft);color:var(--primary);font-size:16px;display:flex;align-items:center;justify-content:center}.sm-about__body{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.sm-about__name{font-size:14px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-about__version{font-size:11px;color:var(--muted)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21753
+ }
21754
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SettingsMenuComponent, decorators: [{
21755
+ type: Component,
21756
+ args: [{ selector: 'app-settings-menu', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"sm-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Menu\">\n <header class=\"sm-header\">\n <h1 class=\"sm-title\">{{ appName() }} d\u00E0nh cho <em>{{ babyName() }}</em></h1>\n <span class=\"sm-spacer\"></span>\n <button type=\"button\" class=\"sm-close\" (click)=\"close.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"sm-body\">\n <!-- \u2500\u2500 Profile card \u2500\u2500 -->\n <button\n type=\"button\"\n class=\"sm-profile\"\n (click)=\"onSelect('edit')\"\n aria-label=\"S\u1EEDa profile b\u00E9\"\n >\n <span class=\"sm-profile__avatar\" aria-hidden=\"true\">{{ initials() }}</span>\n <span class=\"sm-profile__body\">\n <span class=\"sm-profile__name\">{{ babyName() || 'Ch\u01B0a c\u00F3 t\u00EAn' }}</span>\n @if (profileMeta()) {\n <span class=\"sm-profile__meta\">{{ profileMeta() }}</span>\n }\n @if (birthDateLabel()) {\n <span class=\"sm-profile__birth\">Sinh {{ birthDateLabel() }}</span>\n }\n </span>\n <span class=\"sm-profile__edit\">\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </span>\n </button>\n\n <!-- \u2500\u2500 Settings tiles \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">C\u00E0i \u0111\u1EB7t</div>\n <div class=\"sm-tiles\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('settings')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--slate\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-gear\"></i>\n </span>\n <span class=\"sm-tile__label\">C\u00E0i \u0111\u1EB7t chung</span>\n <span class=\"sm-tile__hint\">C\u00E0i \u0111\u1EB7t \u1EE9ng d\u1EE5ng</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out-list')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-clipboard-check\"></i>\n </span>\n <span class=\"sm-tile__label\">\u0110i ch\u01A1i</span>\n <span class=\"sm-tile__hint\">Qu\u1EA3n l\u00FD \u0111\u1ED3 \u0111\u1EA1c</span>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('preparing-purchase')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-cart-shopping\"></i>\n </span>\n <span class=\"sm-tile__label\">Mua s\u1EAFm</span>\n <span class=\"sm-tile__hint\">Checkin \u0111\u1ED3 c\u1EA7n mua</span>\n @if (preparingPendingCount() > 0) {\n <span class=\"sm-tile__badge\">{{ preparingPendingCount() }}</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"sm-tile sm-tile--badge\"\n role=\"menuitem\"\n (click)=\"onSelect('notifications')\"\n >\n <span class=\"sm-tile__icon sm-tile__icon--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-bell\"></i>\n </span>\n <span class=\"sm-tile__label\">Th\u00F4ng b\u00E1o</span>\n <span class=\"sm-tile__hint\">M\u1EDBi &amp; \u0111\u00E3 \u0111\u1ECDc</span>\n @if (unreadCount() > 0) {\n <span class=\"sm-tile__badge\">{{ unreadCount() }}</span>\n }\n </button>\n </div>\n </div>\n\n <!-- \u2500\u2500 Features list \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">T\u00EDnh n\u0103ng</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('daily-summary')\"\n >\n <span class=\"sm-item__well sm-item__well--cyan\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-pie\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">T\u1ED5ng qu\u00E1t v\u1EC1 c\u00E1c ng\u00E0y tr\u01B0\u1EDBc</span>\n <span class=\"sm-item__hint\">Th\u00F4ng tin v\u1EC1 c\u1EEF b\u00FA v\u00E0 c\u00E1c ho\u1EA1t \u0111\u1ED9ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('weight')\">\n <span class=\"sm-item__well sm-item__well--green\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-chart-line\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Th\u1EC3 tr\u1EA1ng</span>\n <span class=\"sm-item__hint\">C\u00E2n n\u1EB7ng, chi\u1EC1u cao, t\u0103ng tr\u01B0\u1EDFng c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button type=\"button\" class=\"sm-item\" role=\"menuitem\" (click)=\"onSelect('mom')\">\n <span class=\"sm-item__well sm-item__well--pink\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-person-pregnant\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">G\u00F3c c\u1EE7a M\u1EB9</span>\n <span class=\"sm-item__hint\">S\u1EE9c kho\u1EBB &amp; dinh d\u01B0\u1EE1ng</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('going-out')\"\n >\n <span class=\"sm-item__well sm-item__well--orange\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-plane-departure\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">{{ goingOutLabel() }}</span>\n <span class=\"sm-item__hint\">{{ goingOutHint() }}</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n\n @if (showDocuments()) {\n <button\n type=\"button\"\n class=\"sm-item\"\n role=\"menuitem\"\n (click)=\"onSelect('documents')\"\n >\n <span class=\"sm-item__well sm-item__well--purple\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-folder\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Explorer</span>\n <span class=\"sm-item__hint\">Kho t\u00E0i li\u1EC7u c\u1EE7a b\u00E9</span>\n </span>\n <i class=\"fa-solid fa-chevron-right sm-item__chev\" aria-hidden=\"true\"></i>\n </button>\n }\n </nav>\n </div>\n\n <!-- \u2500\u2500 About \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Th\u00F4ng tin v\u1EC1 \u1EE9ng d\u1EE5ng</div>\n <section class=\"sm-about\" aria-label=\"About\">\n <div class=\"sm-about__icon\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-circle-info\"></i>\n </div>\n <div class=\"sm-about__body\">\n <span class=\"sm-about__name\">{{ appName() }}</span>\n <span class=\"sm-about__version\">Phi\u00EAn b\u1EA3n {{ appVersion() }}</span>\n </div>\n </section>\n </div>\n\n <!-- \u2500\u2500 Danger \u2500\u2500 -->\n <div class=\"sm-section\">\n <div class=\"sm-section__label\">Danger Zone</div>\n <nav class=\"sm-list sm-list--card\" role=\"menu\">\n <button\n type=\"button\"\n class=\"sm-item sm-item--danger\"\n role=\"menuitem\"\n (click)=\"onSelect('delete')\"\n >\n <span class=\"sm-item__well sm-item__well--rose\" aria-hidden=\"true\">\n <i class=\"fa-solid fa-trash\"></i>\n </span>\n <span class=\"sm-item__body\">\n <span class=\"sm-item__label\">Xo\u00E1 profile</span>\n <span class=\"sm-item__hint\">G\u1EE1 h\u1ED3 s\u01A1 b\u00E9 kh\u1ECFi thi\u1EBFt b\u1ECB n\u00E0y</span>\n </span>\n </button>\n </nav>\n </div>\n </div>\n</section>\n", styles: ["@charset \"UTF-8\";:host{--bg: #f2f3f7;--surface: #ffffff;--border: #e2e5ee;--primary: #5b6cff;--primary-soft: #eef0ff;--accent: #e0338c;--rose: #dc2626;--rose-soft: #fee2e2;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;--cyan: #0891b2;--cyan-soft: #dff7fd;--green: #16a34a;--green-soft: #e4fbec;--pink: #e0338c;--pink-soft: #ffe8f3;--orange: #ea580c;--orange-soft: #fff0e2;--purple: #7c3aed;--purple-soft: #f2e9ff;--slate: #475569;--slate-soft: #f1f5f9;position:fixed;inset:0;z-index:210;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:sm-slide-in .22s cubic-bezier(.22,1,.36,1)}@keyframes sm-slide-in{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.sm-root{display:flex;flex-direction:column;flex:1;min-height:0}.sm-header{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:10px 14px;padding-top:calc(10px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.sm-title{margin:0;font-size:17px;font-weight:700;color:var(--text)}.sm-spacer{flex:1}.sm-close{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:9px;border:none;background:var(--primary-soft);color:var(--primary);font-size:14px;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s,color .15s}.sm-close:hover{filter:brightness(.97)}.sm-body{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:12px 0 calc(24px + env(safe-area-inset-bottom,0px))}.sm-profile{display:flex;align-items:center;gap:12px;width:calc(100% - 24px);margin:0 12px 14px;padding:10px 12px;border:1px solid var(--border);border-radius:16px;background:var(--surface);text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-profile:hover{background:color-mix(in srgb,var(--primary-soft) 40%,var(--surface))}.sm-profile:active{background:var(--primary-soft)}.sm-profile__avatar{flex-shrink:0;width:40px;height:40px;border-radius:14px;background:var(--primary-soft);color:var(--primary);display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:800;letter-spacing:.02em}.sm-profile__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-profile__name{font-size:15px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__meta,.sm-profile__birth{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-profile__birth{color:var(--muted-2);font-size:11px}.sm-profile__edit{flex-shrink:0;display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:700;color:var(--primary);background:var(--primary-soft);padding:4px 8px;border-radius:8px}.sm-profile__edit i{font-size:11px}.sm-section{margin-bottom:14px}.sm-section__label{padding:0 18px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.sm-tiles{margin:0 12px;display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px}.sm-tile{position:relative;display:flex;flex-direction:column;align-items:center;gap:6px;min-height:96px;padding:12px 8px 10px;border:1px solid var(--border);border-radius:14px;background:var(--surface);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-tile:hover{background:color-mix(in srgb,var(--primary-soft) 35%,var(--surface))}.sm-tile:active{background:var(--primary-soft)}.sm-tile__icon{width:38px;height:38px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;font-size:16px}.sm-tile__icon--slate{background:var(--slate-soft);color:var(--slate)}.sm-tile__icon--orange{background:var(--orange-soft);color:var(--orange)}.sm-tile__icon--pink{background:var(--pink-soft);color:var(--pink)}.sm-tile__label{font-size:11px;font-weight:700;color:var(--text);text-align:center;line-height:1.2}.sm-tile__hint{font-size:10px;color:var(--muted-2);text-align:center;line-height:1.2}.sm-tile__badge{position:absolute;top:8px;right:8px;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--accent);color:#fff;font-size:10px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}.sm-list{display:flex;flex-direction:column}.sm-list--card{margin:0 12px;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:var(--surface)}.sm-item{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:none;border-bottom:1px solid var(--border);background:transparent;text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:background .15s}.sm-item:last-child{border-bottom:none}.sm-item:hover{background:var(--primary-soft)}.sm-item:active{background:color-mix(in srgb,var(--primary-soft) 55%,var(--surface))}.sm-item__well{flex-shrink:0;width:36px;height:36px;border-radius:10px;display:inline-flex;align-items:center;justify-content:center;font-size:15px}.sm-item__well--cyan{background:var(--cyan-soft);color:var(--cyan)}.sm-item__well--green{background:var(--green-soft);color:var(--green)}.sm-item__well--pink{background:var(--pink-soft);color:var(--pink)}.sm-item__well--orange{background:var(--orange-soft);color:var(--orange)}.sm-item__well--purple{background:var(--purple-soft);color:var(--purple)}.sm-item__well--rose{background:var(--rose-soft);color:var(--rose)}.sm-item__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.sm-item__label{font-size:14px;font-weight:600;color:var(--text);line-height:1.25}.sm-item__hint{font-size:11px;color:var(--muted-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-item__chev{flex-shrink:0;font-size:13px;color:var(--muted-2)}.sm-item--danger:hover{background:var(--rose-soft)}.sm-item--danger:active{background:color-mix(in srgb,var(--rose-soft) 55%,var(--surface))}.sm-item--danger .sm-item__label{color:var(--rose)}.sm-about{display:flex;align-items:center;gap:12px;margin:0 12px;padding:12px 14px;border-radius:14px;background:var(--surface);border:1px solid var(--border)}.sm-about__icon{flex-shrink:0;width:36px;height:36px;border-radius:10px;background:var(--primary-soft);color:var(--primary);font-size:16px;display:flex;align-items:center;justify-content:center}.sm-about__body{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.sm-about__name{font-size:14px;font-weight:700;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sm-about__version{font-size:11px;color:var(--muted)}\n"] }]
21757
+ }], propDecorators: { appName: [{ type: i0.Input, args: [{ isSignal: true, alias: "appName", required: false }] }], appVersion: [{ type: i0.Input, args: [{ isSignal: true, alias: "appVersion", required: false }] }], showDocuments: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDocuments", required: false }] }], unreadCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "unreadCount", required: false }] }], preparingPendingCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "preparingPendingCount", required: false }] }], babyName: [{ type: i0.Input, args: [{ isSignal: true, alias: "babyName", required: false }] }], ageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ageLabel", required: false }] }], weightLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "weightLabel", required: false }] }], birthDateLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "birthDateLabel", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], select: [{ type: i0.Output, args: ["select"] }] } });
21758
+
21512
21759
  /**
21513
21760
  * Dialog sửa/thêm môn — dùng chung cho trang checklist (có note + trạng thái)
21514
21761
  * và trang cài đặt danh sách (chỉ tiêu đề + mô tả). Khi `canEditTitleDesc`
@@ -21520,6 +21767,7 @@ class GoingOutEditDialogComponent {
21520
21767
  this.heading = 'Sửa món';
21521
21768
  this.showStatus = false;
21522
21769
  this.showNote = false;
21770
+ this.showNotNeeded = false;
21523
21771
  this.canEditTitleDesc = true;
21524
21772
  /** Cho phép trạng thái "1 phần" trong bộ chọn (mặc định true). */
21525
21773
  this.allowPartial = true;
@@ -21527,16 +21775,21 @@ class GoingOutEditDialogComponent {
21527
21775
  this.initialDesc = '';
21528
21776
  this.initialNote = '';
21529
21777
  this.initialStatus = 'none';
21778
+ this.initialNotNeeded = false;
21779
+ /** Cho phép xoá món khỏi checklist chuyến (chỉ checklist). */
21780
+ this.showRemoveFromTrip = false;
21530
21781
  this.cancel = new EventEmitter();
21531
21782
  this.save = new EventEmitter();
21783
+ this.removeFromTrip = new EventEmitter();
21532
21784
  this.draftTitle = signal('', ...(ngDevMode ? [{ debugName: "draftTitle" }] : /* istanbul ignore next */ []));
21533
21785
  this.draftDesc = signal('', ...(ngDevMode ? [{ debugName: "draftDesc" }] : /* istanbul ignore next */ []));
21534
21786
  this.draftNote = signal('', ...(ngDevMode ? [{ debugName: "draftNote" }] : /* istanbul ignore next */ []));
21535
21787
  this.draftStatus = signal('none', ...(ngDevMode ? [{ debugName: "draftStatus" }] : /* istanbul ignore next */ []));
21788
+ this.draftNotNeeded = signal(false, ...(ngDevMode ? [{ debugName: "draftNotNeeded" }] : /* istanbul ignore next */ []));
21536
21789
  this.statuses = [
21537
- { id: 'none', icon: 'pi pi-circle', label: 'Chưa chuẩn bị' },
21538
- { id: 'partial', icon: 'pi pi-circle-fill', label: '1 phần' },
21539
- { id: 'done', icon: 'pi pi-check-circle', label: 'Đã xong' },
21790
+ { id: 'none', icon: 'pi pi-circle', label: 'Chưa' },
21791
+ { id: 'partial', icon: 'pi pi-circle-fill', label: 'Đang làm' },
21792
+ { id: 'done', icon: 'pi pi-check-circle', label: 'Xong' },
21540
21793
  ];
21541
21794
  this.visibleStatuses = computed(() => this.allowPartial ? this.statuses : this.statuses.filter((s) => s.id !== 'partial'), ...(ngDevMode ? [{ debugName: "visibleStatuses" }] : /* istanbul ignore next */ []));
21542
21795
  }
@@ -21545,6 +21798,7 @@ class GoingOutEditDialogComponent {
21545
21798
  this.draftDesc.set(this.initialDesc);
21546
21799
  this.draftNote.set(this.initialNote);
21547
21800
  this.draftStatus.set(this.initialStatus);
21801
+ this.draftNotNeeded.set(this.initialNotNeeded);
21548
21802
  }
21549
21803
  onTitle(v) {
21550
21804
  this.draftTitle.set(v);
@@ -21556,24 +21810,33 @@ class GoingOutEditDialogComponent {
21556
21810
  this.draftNote.set(v);
21557
21811
  }
21558
21812
  pickStatus(s) {
21813
+ this.draftNotNeeded.set(false);
21559
21814
  this.draftStatus.set(s);
21560
21815
  }
21816
+ setNotNeeded(checked) {
21817
+ this.draftNotNeeded.set(checked);
21818
+ if (checked) {
21819
+ this.draftStatus.set('none');
21820
+ }
21821
+ }
21561
21822
  emitSave() {
21562
21823
  if (this.mode === 'add' && !this.draftTitle().trim())
21563
21824
  return;
21825
+ const notNeeded = this.draftNotNeeded();
21564
21826
  this.save.emit({
21565
21827
  title: this.draftTitle(),
21566
21828
  desc: this.draftDesc(),
21567
21829
  note: this.draftNote(),
21568
- status: this.draftStatus(),
21830
+ status: notNeeded ? 'none' : this.draftStatus(),
21831
+ notNeeded,
21569
21832
  });
21570
21833
  }
21571
21834
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GoingOutEditDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21572
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: GoingOutEditDialogComponent, isStandalone: true, selector: "app-going-out-edit-dialog", inputs: { mode: "mode", heading: "heading", showStatus: "showStatus", showNote: "showNote", canEditTitleDesc: "canEditTitleDesc", allowPartial: "allowPartial", initialTitle: "initialTitle", initialDesc: "initialDesc", initialNote: "initialNote", initialStatus: "initialStatus" }, outputs: { cancel: "cancel", save: "save" }, ngImport: i0, template: "<div class=\"go-overlay\" (click)=\"cancel.emit()\" role=\"presentation\">\n <div\n class=\"go-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"heading\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"go-sheet__head\">\n <h2 class=\"go-sheet__title\">{{ heading }}</h2>\n <button type=\"button\" class=\"go-icon-btn\" (click)=\"cancel.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <!-- Ti\u00EAu \u0111\u1EC1 -->\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ti\u00EAu \u0111\u1EC1</span>\n @if (canEditTitleDesc) {\n <input\n class=\"go-input\"\n type=\"text\"\n [value]=\"draftTitle()\"\n (input)=\"onTitle($any($event.target).value)\"\n placeholder=\"VD: T\u00E3 gi\u1EA5y\"\n />\n } @else {\n <p class=\"go-readonly\">{{ draftTitle() }}</p>\n }\n </label>\n\n <!-- M\u00F4 t\u1EA3 -->\n @if (canEditTitleDesc || draftDesc()) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">M\u00F4 t\u1EA3</span>\n @if (canEditTitleDesc) {\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftDesc()\"\n (input)=\"onDesc($any($event.target).value)\"\n placeholder=\"S\u1ED1 l\u01B0\u1EE3ng, l\u01B0u \u00FD khi mang theo\u2026\"\n ></textarea>\n } @else {\n <p class=\"go-readonly go-readonly--muted\">{{ draftDesc() }}</p>\n }\n </label>\n }\n\n <!-- Ghi ch\u00FA (ch\u1EC9 trang checklist) -->\n @if (showNote) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ghi ch\u00FA chuy\u1EBFn \u0111i</span>\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftNote()\"\n (input)=\"onNote($any($event.target).value)\"\n placeholder=\"Ghi ch\u00FA ri\u00EAng cho chuy\u1EBFn n\u00E0y\u2026\"\n ></textarea>\n </label>\n }\n\n <!-- Tr\u1EA1ng th\u00E1i (ch\u1EC9 trang checklist) -->\n @if (showStatus) {\n <div class=\"go-field\">\n <span class=\"go-field__label\">Tr\u1EA1ng th\u00E1i</span>\n <div class=\"go-status-lg\" role=\"radiogroup\" aria-label=\"Tr\u1EA1ng th\u00E1i\">\n @for (s of visibleStatuses(); track s.id) {\n <button\n type=\"button\"\n class=\"go-status-lg__seg\"\n [class.go-status-lg__seg--active]=\"draftStatus() === s.id\"\n (click)=\"pickStatus(s.id)\"\n [attr.aria-pressed]=\"draftStatus() === s.id\"\n >\n <i [class]=\"s.icon\" aria-hidden=\"true\"></i>\n <span>{{ s.label }}</span>\n </button>\n }\n </div>\n </div>\n }\n\n <footer class=\"go-sheet__foot\">\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancel.emit()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"emitSave()\">L\u01B0u</button>\n </footer>\n </div>\n</div>\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:flex-start;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-sheet{width:100%;max-width:460px;background:var(--surface);border-radius:18px;padding:18px 16px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:14px;animation:go-sheet-in .22s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(-16px);opacity:0}to{transform:translateY(0);opacity:1}}.go-sheet__head{display:flex;align-items:center;gap:8px}.go-sheet__title{margin:0;font-size:16px;font-weight:700;flex:1}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.go-field{display:flex;flex-direction:column;gap:6px}.go-field__label{font-size:12px;font-weight:600;color:var(--muted)}.go-input{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font-size:15px;font-family:inherit;outline:none;transition:border-color .15s}.go-input:focus{border-color:var(--primary)}.go-input--area{resize:vertical;min-height:56px}.go-readonly{margin:0;font-size:15px;font-weight:600;color:var(--text)}.go-readonly--muted{font-weight:400;font-size:14px;color:var(--muted)}.go-status-lg{display:flex;gap:6px;border:1px solid var(--border);border-radius:12px;padding:4px;background:var(--bg)}.go-status-lg__seg{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 4px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-status-lg__seg i{font-size:16px}.go-status-lg__seg--active{background:var(--primary);color:#fff}.go-sheet__foot{display:flex;justify-content:flex-end;gap:8px;margin-top:2px}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21835
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: GoingOutEditDialogComponent, isStandalone: true, selector: "app-going-out-edit-dialog", inputs: { mode: "mode", heading: "heading", showStatus: "showStatus", showNote: "showNote", showNotNeeded: "showNotNeeded", canEditTitleDesc: "canEditTitleDesc", allowPartial: "allowPartial", initialTitle: "initialTitle", initialDesc: "initialDesc", initialNote: "initialNote", initialStatus: "initialStatus", initialNotNeeded: "initialNotNeeded", showRemoveFromTrip: "showRemoveFromTrip" }, outputs: { cancel: "cancel", save: "save", removeFromTrip: "removeFromTrip" }, ngImport: i0, template: "<div class=\"go-overlay\" (click)=\"cancel.emit()\" role=\"presentation\">\n <div\n class=\"go-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"heading\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"go-sheet__head\">\n <h2 class=\"go-sheet__title\">{{ heading }}</h2>\n <button type=\"button\" class=\"go-icon-btn\" (click)=\"cancel.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <!-- Ti\u00EAu \u0111\u1EC1 -->\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ti\u00EAu \u0111\u1EC1</span>\n @if (canEditTitleDesc) {\n <input\n class=\"go-input\"\n type=\"text\"\n [value]=\"draftTitle()\"\n (input)=\"onTitle($any($event.target).value)\"\n placeholder=\"VD: T\u00E3 gi\u1EA5y\"\n />\n } @else {\n <p class=\"go-readonly\">{{ draftTitle() }}</p>\n }\n </label>\n\n <!-- M\u00F4 t\u1EA3 -->\n @if (canEditTitleDesc || draftDesc()) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">M\u00F4 t\u1EA3</span>\n @if (canEditTitleDesc) {\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftDesc()\"\n (input)=\"onDesc($any($event.target).value)\"\n placeholder=\"S\u1ED1 l\u01B0\u1EE3ng, l\u01B0u \u00FD khi mang theo\u2026\"\n ></textarea>\n } @else {\n <p class=\"go-readonly go-readonly--muted\">{{ draftDesc() }}</p>\n }\n </label>\n }\n\n <!-- Ghi ch\u00FA (ch\u1EC9 trang checklist) -->\n @if (showNote) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ghi ch\u00FA chuy\u1EBFn \u0111i</span>\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftNote()\"\n (input)=\"onNote($any($event.target).value)\"\n placeholder=\"Ghi ch\u00FA ri\u00EAng cho chuy\u1EBFn n\u00E0y\u2026\"\n ></textarea>\n </label>\n }\n\n <!-- Tr\u1EA1ng th\u00E1i (ch\u1EC9 trang checklist) -->\n @if (showStatus) {\n <div class=\"go-field\">\n <span class=\"go-field__label\">Tr\u1EA1ng th\u00E1i</span>\n <div\n class=\"go-status-lg\"\n role=\"radiogroup\"\n aria-label=\"Tr\u1EA1ng th\u00E1i\"\n [class.go-status-lg--disabled]=\"draftNotNeeded()\"\n >\n @for (s of visibleStatuses(); track s.id) {\n <button\n type=\"button\"\n class=\"go-status-lg__seg\"\n [class.go-status-lg__seg--active]=\"draftStatus() === s.id && !draftNotNeeded()\"\n (click)=\"pickStatus(s.id)\"\n [attr.aria-pressed]=\"draftStatus() === s.id && !draftNotNeeded()\"\n [disabled]=\"draftNotNeeded()\"\n >\n <i [class]=\"s.icon\" aria-hidden=\"true\"></i>\n <span>{{ s.label }}</span>\n </button>\n }\n </div>\n </div>\n }\n\n @if (showNotNeeded) {\n <label class=\"go-toggle-row\">\n <span class=\"go-toggle-row__text\">Kh\u00F4ng c\u1EA7n mang l\u1EA7n n\u00E0y</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"draftNotNeeded()\"\n (change)=\"setNotNeeded($any($event.target).checked)\"\n />\n </label>\n }\n\n <footer class=\"go-sheet__foot\">\n @if (showRemoveFromTrip) {\n <button type=\"button\" class=\"go-btn go-btn--danger\" (click)=\"removeFromTrip.emit()\">\n B\u1ECF kh\u1ECFi chuy\u1EBFn\n </button>\n }\n <span class=\"go-sheet__foot-spacer\"></span>\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancel.emit()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"emitSave()\">\n {{ mode === 'add' ? 'Th\u00EAm' : 'L\u01B0u' }}\n </button>\n </footer>\n </div>\n</div>\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));padding-bottom:calc(16px + env(safe-area-inset-bottom,0px));animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-sheet{width:100%;max-width:460px;max-height:min(85vh,640px);overflow:auto;background:var(--surface);border-radius:18px;padding:18px 16px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:14px;animation:go-sheet-in .22s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(12px);opacity:0}to{transform:translateY(0);opacity:1}}.go-sheet__head{display:flex;align-items:center;gap:8px}.go-sheet__title{margin:0;font-size:16px;font-weight:700;flex:1}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.go-field{display:flex;flex-direction:column;gap:6px}.go-field__label{font-size:12px;font-weight:600;color:var(--muted)}.go-input{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font-size:15px;font-family:inherit;outline:none;transition:border-color .15s}.go-input:focus{border-color:var(--primary)}.go-input--area{resize:vertical;min-height:56px}.go-readonly{margin:0;font-size:15px;font-weight:600;color:var(--text)}.go-readonly--muted{font-weight:400;font-size:14px;color:var(--muted)}.go-status-lg{display:flex;gap:6px;border:1px solid var(--border);border-radius:12px;padding:4px;background:var(--bg)}.go-status-lg--disabled{opacity:.5}.go-status-lg__seg{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 4px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-status-lg__seg i{font-size:16px}.go-status-lg__seg--active{background:var(--primary);color:#fff}.go-toggle-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--border);border-radius:12px;background:var(--bg);cursor:pointer}.go-toggle-row__text{font-size:14px;font-weight:600;color:var(--text)}.go-sheet__foot{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:2px}.go-sheet__foot-spacer{flex:1}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}.go-btn--danger{background:transparent;color:#dc2626;padding-left:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21573
21836
  }
21574
21837
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GoingOutEditDialogComponent, decorators: [{
21575
21838
  type: Component,
21576
- args: [{ selector: 'app-going-out-edit-dialog', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"go-overlay\" (click)=\"cancel.emit()\" role=\"presentation\">\n <div\n class=\"go-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"heading\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"go-sheet__head\">\n <h2 class=\"go-sheet__title\">{{ heading }}</h2>\n <button type=\"button\" class=\"go-icon-btn\" (click)=\"cancel.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <!-- Ti\u00EAu \u0111\u1EC1 -->\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ti\u00EAu \u0111\u1EC1</span>\n @if (canEditTitleDesc) {\n <input\n class=\"go-input\"\n type=\"text\"\n [value]=\"draftTitle()\"\n (input)=\"onTitle($any($event.target).value)\"\n placeholder=\"VD: T\u00E3 gi\u1EA5y\"\n />\n } @else {\n <p class=\"go-readonly\">{{ draftTitle() }}</p>\n }\n </label>\n\n <!-- M\u00F4 t\u1EA3 -->\n @if (canEditTitleDesc || draftDesc()) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">M\u00F4 t\u1EA3</span>\n @if (canEditTitleDesc) {\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftDesc()\"\n (input)=\"onDesc($any($event.target).value)\"\n placeholder=\"S\u1ED1 l\u01B0\u1EE3ng, l\u01B0u \u00FD khi mang theo\u2026\"\n ></textarea>\n } @else {\n <p class=\"go-readonly go-readonly--muted\">{{ draftDesc() }}</p>\n }\n </label>\n }\n\n <!-- Ghi ch\u00FA (ch\u1EC9 trang checklist) -->\n @if (showNote) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ghi ch\u00FA chuy\u1EBFn \u0111i</span>\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftNote()\"\n (input)=\"onNote($any($event.target).value)\"\n placeholder=\"Ghi ch\u00FA ri\u00EAng cho chuy\u1EBFn n\u00E0y\u2026\"\n ></textarea>\n </label>\n }\n\n <!-- Tr\u1EA1ng th\u00E1i (ch\u1EC9 trang checklist) -->\n @if (showStatus) {\n <div class=\"go-field\">\n <span class=\"go-field__label\">Tr\u1EA1ng th\u00E1i</span>\n <div class=\"go-status-lg\" role=\"radiogroup\" aria-label=\"Tr\u1EA1ng th\u00E1i\">\n @for (s of visibleStatuses(); track s.id) {\n <button\n type=\"button\"\n class=\"go-status-lg__seg\"\n [class.go-status-lg__seg--active]=\"draftStatus() === s.id\"\n (click)=\"pickStatus(s.id)\"\n [attr.aria-pressed]=\"draftStatus() === s.id\"\n >\n <i [class]=\"s.icon\" aria-hidden=\"true\"></i>\n <span>{{ s.label }}</span>\n </button>\n }\n </div>\n </div>\n }\n\n <footer class=\"go-sheet__foot\">\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancel.emit()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"emitSave()\">L\u01B0u</button>\n </footer>\n </div>\n</div>\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:flex-start;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-sheet{width:100%;max-width:460px;background:var(--surface);border-radius:18px;padding:18px 16px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:14px;animation:go-sheet-in .22s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(-16px);opacity:0}to{transform:translateY(0);opacity:1}}.go-sheet__head{display:flex;align-items:center;gap:8px}.go-sheet__title{margin:0;font-size:16px;font-weight:700;flex:1}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.go-field{display:flex;flex-direction:column;gap:6px}.go-field__label{font-size:12px;font-weight:600;color:var(--muted)}.go-input{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font-size:15px;font-family:inherit;outline:none;transition:border-color .15s}.go-input:focus{border-color:var(--primary)}.go-input--area{resize:vertical;min-height:56px}.go-readonly{margin:0;font-size:15px;font-weight:600;color:var(--text)}.go-readonly--muted{font-weight:400;font-size:14px;color:var(--muted)}.go-status-lg{display:flex;gap:6px;border:1px solid var(--border);border-radius:12px;padding:4px;background:var(--bg)}.go-status-lg__seg{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 4px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-status-lg__seg i{font-size:16px}.go-status-lg__seg--active{background:var(--primary);color:#fff}.go-sheet__foot{display:flex;justify-content:flex-end;gap:8px;margin-top:2px}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}\n"] }]
21839
+ args: [{ selector: 'app-going-out-edit-dialog', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"go-overlay\" (click)=\"cancel.emit()\" role=\"presentation\">\n <div\n class=\"go-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"heading\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"go-sheet__head\">\n <h2 class=\"go-sheet__title\">{{ heading }}</h2>\n <button type=\"button\" class=\"go-icon-btn\" (click)=\"cancel.emit()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <!-- Ti\u00EAu \u0111\u1EC1 -->\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ti\u00EAu \u0111\u1EC1</span>\n @if (canEditTitleDesc) {\n <input\n class=\"go-input\"\n type=\"text\"\n [value]=\"draftTitle()\"\n (input)=\"onTitle($any($event.target).value)\"\n placeholder=\"VD: T\u00E3 gi\u1EA5y\"\n />\n } @else {\n <p class=\"go-readonly\">{{ draftTitle() }}</p>\n }\n </label>\n\n <!-- M\u00F4 t\u1EA3 -->\n @if (canEditTitleDesc || draftDesc()) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">M\u00F4 t\u1EA3</span>\n @if (canEditTitleDesc) {\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftDesc()\"\n (input)=\"onDesc($any($event.target).value)\"\n placeholder=\"S\u1ED1 l\u01B0\u1EE3ng, l\u01B0u \u00FD khi mang theo\u2026\"\n ></textarea>\n } @else {\n <p class=\"go-readonly go-readonly--muted\">{{ draftDesc() }}</p>\n }\n </label>\n }\n\n <!-- Ghi ch\u00FA (ch\u1EC9 trang checklist) -->\n @if (showNote) {\n <label class=\"go-field\">\n <span class=\"go-field__label\">Ghi ch\u00FA chuy\u1EBFn \u0111i</span>\n <textarea\n class=\"go-input go-input--area\"\n rows=\"2\"\n [value]=\"draftNote()\"\n (input)=\"onNote($any($event.target).value)\"\n placeholder=\"Ghi ch\u00FA ri\u00EAng cho chuy\u1EBFn n\u00E0y\u2026\"\n ></textarea>\n </label>\n }\n\n <!-- Tr\u1EA1ng th\u00E1i (ch\u1EC9 trang checklist) -->\n @if (showStatus) {\n <div class=\"go-field\">\n <span class=\"go-field__label\">Tr\u1EA1ng th\u00E1i</span>\n <div\n class=\"go-status-lg\"\n role=\"radiogroup\"\n aria-label=\"Tr\u1EA1ng th\u00E1i\"\n [class.go-status-lg--disabled]=\"draftNotNeeded()\"\n >\n @for (s of visibleStatuses(); track s.id) {\n <button\n type=\"button\"\n class=\"go-status-lg__seg\"\n [class.go-status-lg__seg--active]=\"draftStatus() === s.id && !draftNotNeeded()\"\n (click)=\"pickStatus(s.id)\"\n [attr.aria-pressed]=\"draftStatus() === s.id && !draftNotNeeded()\"\n [disabled]=\"draftNotNeeded()\"\n >\n <i [class]=\"s.icon\" aria-hidden=\"true\"></i>\n <span>{{ s.label }}</span>\n </button>\n }\n </div>\n </div>\n }\n\n @if (showNotNeeded) {\n <label class=\"go-toggle-row\">\n <span class=\"go-toggle-row__text\">Kh\u00F4ng c\u1EA7n mang l\u1EA7n n\u00E0y</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"draftNotNeeded()\"\n (change)=\"setNotNeeded($any($event.target).checked)\"\n />\n </label>\n }\n\n <footer class=\"go-sheet__foot\">\n @if (showRemoveFromTrip) {\n <button type=\"button\" class=\"go-btn go-btn--danger\" (click)=\"removeFromTrip.emit()\">\n B\u1ECF kh\u1ECFi chuy\u1EBFn\n </button>\n }\n <span class=\"go-sheet__foot-spacer\"></span>\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancel.emit()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"emitSave()\">\n {{ mode === 'add' ? 'Th\u00EAm' : 'L\u01B0u' }}\n </button>\n </footer>\n </div>\n</div>\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;color:var(--text)}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a73;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));padding-bottom:calc(16px + env(safe-area-inset-bottom,0px));animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-sheet{width:100%;max-width:460px;max-height:min(85vh,640px);overflow:auto;background:var(--surface);border-radius:18px;padding:18px 16px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:14px;animation:go-sheet-in .22s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(12px);opacity:0}to{transform:translateY(0);opacity:1}}.go-sheet__head{display:flex;align-items:center;gap:8px}.go-sheet__title{margin:0;font-size:16px;font-weight:700;flex:1}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.go-field{display:flex;flex-direction:column;gap:6px}.go-field__label{font-size:12px;font-weight:600;color:var(--muted)}.go-input{width:100%;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--bg);color:var(--text);font-size:15px;font-family:inherit;outline:none;transition:border-color .15s}.go-input:focus{border-color:var(--primary)}.go-input--area{resize:vertical;min-height:56px}.go-readonly{margin:0;font-size:15px;font-weight:600;color:var(--text)}.go-readonly--muted{font-weight:400;font-size:14px;color:var(--muted)}.go-status-lg{display:flex;gap:6px;border:1px solid var(--border);border-radius:12px;padding:4px;background:var(--bg)}.go-status-lg--disabled{opacity:.5}.go-status-lg__seg{flex:1;display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 4px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:12px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-status-lg__seg i{font-size:16px}.go-status-lg__seg--active{background:var(--primary);color:#fff}.go-toggle-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border:1px solid var(--border);border-radius:12px;background:var(--bg);cursor:pointer}.go-toggle-row__text{font-size:14px;font-weight:600;color:var(--text)}.go-sheet__foot{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:2px}.go-sheet__foot-spacer{flex:1}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}.go-btn--danger{background:transparent;color:#dc2626;padding-left:0}\n"] }]
21577
21840
  }], propDecorators: { mode: [{
21578
21841
  type: Input
21579
21842
  }], heading: [{
@@ -21582,6 +21845,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21582
21845
  type: Input
21583
21846
  }], showNote: [{
21584
21847
  type: Input
21848
+ }], showNotNeeded: [{
21849
+ type: Input
21585
21850
  }], canEditTitleDesc: [{
21586
21851
  type: Input
21587
21852
  }], allowPartial: [{
@@ -21594,10 +21859,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
21594
21859
  type: Input
21595
21860
  }], initialStatus: [{
21596
21861
  type: Input
21862
+ }], initialNotNeeded: [{
21863
+ type: Input
21864
+ }], showRemoveFromTrip: [{
21865
+ type: Input
21597
21866
  }], cancel: [{
21598
21867
  type: Output
21599
21868
  }], save: [{
21600
21869
  type: Output
21870
+ }], removeFromTrip: [{
21871
+ type: Output
21601
21872
  }] } });
21602
21873
 
21603
21874
  class GoingOutChecklistComponent {
@@ -21619,6 +21890,9 @@ class GoingOutChecklistComponent {
21619
21890
  this.allowAdd = this.svc.allowAdd;
21620
21891
  this.allowPartial = this.svc.allowPartial;
21621
21892
  this.sheetLoading = this.svc.sheetLoading;
21893
+ this.activeTripCount = this.svc.activeTripCount;
21894
+ this.packedCount = this.svc.packedCount;
21895
+ this.progressPercent = this.svc.progressPercent;
21622
21896
  this.title = computed(() => {
21623
21897
  const name = this.babyName().trim();
21624
21898
  return name ? `Chuẩn bị cho ${name} ra ngoài` : 'Chuẩn bị đi ra ngoài';
@@ -21626,10 +21900,22 @@ class GoingOutChecklistComponent {
21626
21900
  this.editingItem = signal(null, ...(ngDevMode ? [{ debugName: "editingItem" }] : /* istanbul ignore next */ []));
21627
21901
  this.adding = signal(false, ...(ngDevMode ? [{ debugName: "adding" }] : /* istanbul ignore next */ []));
21628
21902
  this.resetConfirm = signal(false, ...(ngDevMode ? [{ debugName: "resetConfirm" }] : /* istanbul ignore next */ []));
21903
+ this.headerMenuOpen = signal(false, ...(ngDevMode ? [{ debugName: "headerMenuOpen" }] : /* istanbul ignore next */ []));
21904
+ this.draggingId = signal(null, ...(ngDevMode ? [{ debugName: "draggingId" }] : /* istanbul ignore next */ []));
21905
+ this.dragOverId = signal(null, ...(ngDevMode ? [{ debugName: "dragOverId" }] : /* istanbul ignore next */ []));
21629
21906
  this.isDialogOpen = computed(() => this.editingItem() !== null || this.adding(), ...(ngDevMode ? [{ debugName: "isDialogOpen" }] : /* istanbul ignore next */ []));
21630
21907
  this.dialogMode = computed(() => (this.adding() ? 'add' : 'edit'), ...(ngDevMode ? [{ debugName: "dialogMode" }] : /* istanbul ignore next */ []));
21631
- this.dialogHeading = computed(() => (this.adding() ? 'Thêm món' : 'Sửa món'), ...(ngDevMode ? [{ debugName: "dialogHeading" }] : /* istanbul ignore next */ []));
21908
+ this.dialogHeading = computed(() => this.adding() ? 'Thêm món cho chuyến này' : 'Chi tiết món', ...(ngDevMode ? [{ debugName: "dialogHeading" }] : /* istanbul ignore next */ []));
21632
21909
  this.dialogCanEditTitleDesc = computed(() => this.adding() || this.allowEdit(), ...(ngDevMode ? [{ debugName: "dialogCanEditTitleDesc" }] : /* istanbul ignore next */ []));
21910
+ this.dialogShowRemove = computed(() => !this.adding() && this.allowEdit() && this.editingItem() !== null, ...(ngDevMode ? [{ debugName: "dialogShowRemove" }] : /* istanbul ignore next */ []));
21911
+ this.heroTitle = computed(() => {
21912
+ if (this.allDone())
21913
+ return 'Sẵn sàng — đi thôi!';
21914
+ const pct = this.progressPercent();
21915
+ if (pct >= 70)
21916
+ return 'Gần xong rồi!';
21917
+ return 'Đang chuẩn bị…';
21918
+ }, ...(ngDevMode ? [{ debugName: "heroTitle" }] : /* istanbul ignore next */ []));
21633
21919
  this.statusLabels = {
21634
21920
  none: 'Chưa chuẩn bị',
21635
21921
  partial: 'Đang chuẩn bị 1 phần',
@@ -21659,13 +21945,69 @@ class GoingOutChecklistComponent {
21659
21945
  const next = order[(order.indexOf(item.status) % order.length + 1) % order.length];
21660
21946
  this.svc.setTripStatus(item.id, next);
21661
21947
  }
21662
- toggleNotNeeded(item, ev) {
21663
- ev.stopPropagation();
21664
- this.svc.toggleNotNeeded(item.id);
21665
- }
21666
21948
  statusLabel(status) {
21667
21949
  return this.statusLabels[status];
21668
21950
  }
21951
+ itemStatusBadge(item) {
21952
+ if (item.notNeeded)
21953
+ return 'Không mang';
21954
+ if (item.status === 'done')
21955
+ return 'Đã gói';
21956
+ if (item.status === 'partial')
21957
+ return 'Đang làm';
21958
+ return 'Chưa';
21959
+ }
21960
+ toggleHeaderMenu() {
21961
+ this.headerMenuOpen.update((v) => !v);
21962
+ }
21963
+ closeHeaderMenu() {
21964
+ this.headerMenuOpen.set(false);
21965
+ }
21966
+ onManageFromMenu() {
21967
+ this.closeHeaderMenu();
21968
+ this.manage.emit();
21969
+ }
21970
+ onResetFromMenu() {
21971
+ this.closeHeaderMenu();
21972
+ this.askReset();
21973
+ }
21974
+ moveItem(index, delta, ev) {
21975
+ ev.stopPropagation();
21976
+ this.svc.moveTripItem(index, delta);
21977
+ }
21978
+ onDragStart(ev, item) {
21979
+ this.draggingId.set(item.id);
21980
+ ev.dataTransfer?.setData('text/plain', item.id);
21981
+ if (ev.dataTransfer) {
21982
+ ev.dataTransfer.effectAllowed = 'move';
21983
+ }
21984
+ }
21985
+ onDragOver(ev, item) {
21986
+ ev.preventDefault();
21987
+ if (this.draggingId() !== item.id) {
21988
+ this.dragOverId.set(item.id);
21989
+ }
21990
+ }
21991
+ onDrop(ev, item) {
21992
+ ev.preventDefault();
21993
+ const fromId = this.draggingId() ?? ev.dataTransfer?.getData('text/plain');
21994
+ if (fromId) {
21995
+ this.svc.reorderTrip(fromId, item.id);
21996
+ }
21997
+ this.onDragEnd();
21998
+ }
21999
+ onDragEnd() {
22000
+ this.draggingId.set(null);
22001
+ this.dragOverId.set(null);
22002
+ }
22003
+ openDetail(item, ev) {
22004
+ ev.stopPropagation();
22005
+ this.openEdit(item);
22006
+ }
22007
+ onMainTap(item, ev) {
22008
+ ev.stopPropagation();
22009
+ this.cycleStatus(item);
22010
+ }
21669
22011
  openAdd() {
21670
22012
  this.editingItem.set(null);
21671
22013
  this.adding.set(true);
@@ -21680,7 +22022,7 @@ class GoingOutChecklistComponent {
21680
22022
  }
21681
22023
  onSaveEdit(payload) {
21682
22024
  if (this.adding()) {
21683
- this.svc.addChecklistItem(payload.title, payload.desc, payload.note, payload.status);
22025
+ this.svc.addChecklistItem(payload.title, payload.desc, payload.note, payload.status, payload.notNeeded);
21684
22026
  }
21685
22027
  else {
21686
22028
  const item = this.editingItem();
@@ -21690,14 +22032,14 @@ class GoingOutChecklistComponent {
21690
22032
  }
21691
22033
  this.closeDialog();
21692
22034
  }
21693
- /** Ẩn môn khỏi chuyến đi này — danh sách mẫu giữ nguyên; reload hoặc đặt lại mặc định sẽ hiện lại. */
21694
- removeItem(item, ev) {
21695
- ev.stopPropagation();
21696
- if (!this.allowEdit())
22035
+ removeFromDialog() {
22036
+ const item = this.editingItem();
22037
+ if (!item || !this.allowEdit())
21697
22038
  return;
21698
22039
  if (!confirm(`Bỏ "${item.title}" khỏi checklist lần này?`))
21699
22040
  return;
21700
22041
  this.svc.removeFromTrip(item.id);
22042
+ this.closeDialog();
21701
22043
  }
21702
22044
  askReset() {
21703
22045
  this.resetConfirm.set(true);
@@ -21710,11 +22052,11 @@ class GoingOutChecklistComponent {
21710
22052
  this.resetConfirm.set(false);
21711
22053
  }
21712
22054
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GoingOutChecklistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21713
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: GoingOutChecklistComponent, isStandalone: true, selector: "app-going-out-checklist", inputs: { babyName: { classPropertyName: "babyName", publicName: "babyName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back", manage: "manage" }, ngImport: i0, template: "<section class=\"go-root\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"title()\">\n <header class=\"go-header\">\n <button type=\"button\" class=\"go-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"go-header__title\">{{ title() }}</h1>\n <span class=\"go-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"go-header-btn\"\n (click)=\"refresh()\"\n [disabled]=\"sheetLoading()\"\n title=\"L\u1EA5y checklist m\u1EDBi nh\u1EA5t t\u1EEB Google Sheet (\u0111\u1ED3ng b\u1ED9 v\u1EDBi ng\u01B0\u1EDDi kh\u00E1c)\"\n aria-label=\"\u0110\u1ED3ng b\u1ED9 t\u1EEB Google Sheet\"\n >\n <i class=\"pi pi-cloud-download\" [class.go-header-btn__spin]=\"sheetLoading()\" aria-hidden=\"true\"></i>\n <span>\u0110\u1ED3ng b\u1ED9</span>\n </button>\n <button\n type=\"button\"\n class=\"go-header-btn go-header-btn--muted\"\n (click)=\"askReset()\"\n title=\"\u0110\u1EB7t l\u1EA1i checklist: tick, ghi ch\u00FA, m\u00F3n \u0111\u00E3 \u1EA9n ho\u1EB7c th\u00EAm t\u1EA1m\"\n aria-label=\"\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\"\n >\n <i class=\"pi pi-undo\" aria-hidden=\"true\"></i>\n <span>\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh</span>\n </button>\n <button\n type=\"button\"\n class=\"go-icon-btn\"\n (click)=\"manage.emit()\"\n title=\"Qu\u1EA3n l\u00FD danh s\u00E1ch m\u00F3n m\u1EABu\"\n aria-label=\"Qu\u1EA3n l\u00FD danh s\u00E1ch\"\n >\n <i class=\"pi pi-list\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n @if (allDone()) {\n <div class=\"go-ready\" role=\"status\">\n <i class=\"pi pi-check-circle\" aria-hidden=\"true\"></i>\n <span>S\u1EB5n s\u00E0ng \u2014 mang \u0111\u1ED3 \u0111i th\u00F4i</span>\n </div>\n }\n\n <div class=\"go-progress\">\n <div class=\"go-progress__row\">\n <span class=\"go-progress__count\">{{ doneCount() }}/{{ total() }}</span>\n <div class=\"go-progress__meta\">\n <span class=\"go-progress__state\" [class.go-progress__state--done]=\"allDone()\">\n {{ allDone() ? 'S\u1EB5n s\u00E0ng \u0111i r\u1ED3i!' : '\u0110ang chu\u1EA9n b\u1ECB\u2026' }}\n </span>\n <span class=\"go-progress__sub\">\n {{ readyCount() }} \u0111ang l\u00E0m \u00B7 {{ total() - readyCount() }} ch\u01B0a b\u1EAFt \u0111\u1EA7u\n @if (notNeededCount() > 0) {\n \u00B7 {{ notNeededCount() }} kh\u00F4ng c\u1EA7n\n }\n </span>\n </div>\n </div>\n <div class=\"go-bar\"><span class=\"go-bar__fill\" [style.width.%]=\"total() ? (doneCount() / total()) * 100 : 0\"></span></div>\n </div>\n\n <div class=\"go-list\">\n @for (item of items(); track item.id) {\n <div\n class=\"go-row\"\n [class.go-row--not-needed]=\"item.notNeeded\"\n (click)=\"cycleStatus(item)\"\n >\n <button\n type=\"button\"\n class=\"go-check\"\n [class.go-check--none]=\"item.status === 'none' && !item.notNeeded\"\n [class.go-check--partial]=\"item.status === 'partial'\"\n [class.go-check--done]=\"item.status === 'done'\"\n [class.go-check--skipped]=\"item.notNeeded\"\n (click)=\"cycleStatus(item); $event.stopPropagation()\"\n [attr.aria-label]=\"item.notNeeded ? 'Kh\u00F4ng c\u1EA7n l\u1EA7n n\u00E0y' : statusLabel(item.status)\"\n [attr.aria-pressed]=\"item.notNeeded || item.status !== 'none'\"\n [disabled]=\"item.notNeeded\"\n >\n @if (!item.notNeeded && item.status === 'done') {\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n }\n </button>\n\n <div class=\"go-row__body\" (click)=\"cycleStatus(item); $event.stopPropagation()\">\n <span\n class=\"go-row__title\"\n [class.go-row__title--done]=\"item.status === 'done'\"\n [class.go-row__title--skipped]=\"item.notNeeded\"\n >{{ item.title }}</span>\n @if (item.desc) {\n <span class=\"go-row__desc\">{{ item.desc }}</span>\n }\n @if (item.note) {\n <span class=\"go-row__note\">\n <i class=\"pi pi-file-edit\" aria-hidden=\"true\"></i>\n {{ item.note }}\n </span>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__skip\"\n [class.go-row__skip--on]=\"item.notNeeded\"\n (click)=\"toggleNotNeeded(item, $event)\"\n [title]=\"item.notNeeded ? 'C\u1EA7n mang l\u1EA1i l\u1EA7n n\u00E0y' : 'Kh\u00F4ng c\u1EA7n mang l\u1EA7n n\u00E0y'\"\n [attr.aria-label]=\"item.notNeeded ? 'C\u1EA7n mang l\u1EA1i l\u1EA7n n\u00E0y' : 'Kh\u00F4ng c\u1EA7n mang l\u1EA7n n\u00E0y'\"\n [attr.aria-pressed]=\"item.notNeeded\"\n >\n <i class=\"pi pi-ban\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__edit\"\n (click)=\"openEdit(item); $event.stopPropagation()\"\n title=\"Ghi ch\u00FA / s\u1EEDa\"\n aria-label=\"Ghi ch\u00FA / s\u1EEDa\"\n >\n <i class=\"pi pi-pencil\" aria-hidden=\"true\"></i>\n </button>\n\n @if (allowEdit()) {\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__danger\"\n (click)=\"removeItem(item, $event)\"\n title=\"B\u1ECF kh\u1ECFi checklist l\u1EA7n n\u00E0y\"\n aria-label=\"B\u1ECF kh\u1ECFi checklist l\u1EA7n n\u00E0y\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n } @empty {\n <p class=\"go-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o.</p>\n }\n </div>\n\n @if (allowAdd()) {\n <div class=\"go-add-wrap\">\n <button type=\"button\" class=\"go-add\" (click)=\"openAdd()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n }\n</section>\n\n@if (isDialogOpen()) {\n <app-going-out-edit-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [showStatus]=\"true\"\n [showNote]=\"true\"\n [canEditTitleDesc]=\"dialogCanEditTitleDesc()\"\n [allowPartial]=\"allowPartial()\"\n [initialTitle]=\"editingItem()?.title ?? ''\"\n [initialDesc]=\"editingItem()?.desc ?? ''\"\n [initialNote]=\"editingItem()?.note ?? ''\"\n [initialStatus]=\"editingItem()?.status ?? 'none'\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSaveEdit($event)\"\n />\n}\n\n@if (resetConfirm()) {\n <div class=\"go-overlay\" (click)=\"cancelReset()\" role=\"presentation\">\n <div class=\"go-confirm\" role=\"alertdialog\" aria-modal=\"true\" aria-label=\"\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\" (click)=\"$event.stopPropagation()\">\n <h2 class=\"go-confirm__title\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh?</h2>\n <p class=\"go-confirm__body\">\n Checklist s\u1EBD tr\u1EDF l\u1EA1i nh\u01B0 danh s\u00E1ch m\u1EABu: b\u1ECF tick, xo\u00E1 ghi ch\u00FA, b\u1ECF \u0111\u00E1nh d\u1EA5u kh\u00F4ng c\u1EA7n, hi\u1EC7n l\u1EA1i m\u00F3n \u0111\u00E3 \u1EA9n v\u00E0 b\u1ECF m\u00F3n ch\u1EC9 th\u00EAm cho l\u1EA7n \u0111i n\u00E0y.\n Danh s\u00E1ch m\u1EABu trong c\u00E0i \u0111\u1EB7t kh\u00F4ng thay \u0111\u1ED5i.\n </p>\n <div class=\"go-confirm__foot\">\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancelReset()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"confirmReset()\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh</button>\n </div>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:go-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes go-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.go-root{display:flex;flex-direction:column;flex:1;min-height:0}.go-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.go-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-back:hover{background:var(--primary-soft);color:var(--primary)}.go-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.go-header__spacer{flex:1}.go-header-btn{flex-shrink:0;display:inline-flex;align-items:center;gap:4px;height:30px;padding:0 8px;border:1px solid var(--border);border-radius:8px;background:var(--surface);color:var(--primary);font-size:11px;font-weight:700;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-header-btn i{font-size:12px}.go-header-btn:hover:not(:disabled){background:var(--primary-soft);border-color:color-mix(in srgb,var(--primary) 35%,var(--border))}.go-header-btn:disabled{opacity:.55;cursor:default}.go-header-btn--muted{color:var(--muted)}.go-header-btn--muted:hover:not(:disabled){background:#f1f5f9;border-color:var(--border);color:var(--text)}.go-header-btn__spin{animation:go-refresh-spin .8s linear infinite}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover:not(:disabled){background:var(--primary-soft);color:var(--primary)}.go-icon-btn:disabled{opacity:.55;cursor:default}.go-icon-btn__spin{animation:go-refresh-spin .8s linear infinite}@keyframes go-refresh-spin{to{transform:rotate(360deg)}}.go-ready{display:flex;align-items:center;gap:8px;margin:12px;padding:8px 14px;background:#e9f9ef;border:1px solid #b6e7c4;border-radius:12px;color:#15803d;font-size:13px;font-weight:600}.go-ready i{font-size:16px}.go-progress{margin:0 12px 10px;padding:12px 14px;background:var(--surface);border:1px solid var(--border);border-radius:14px}.go-progress__row{display:flex;align-items:center;gap:12px;margin-bottom:10px}.go-progress__count{font-size:22px;font-weight:800;color:var(--text);line-height:1}.go-progress__meta{display:flex;flex-direction:column;gap:2px}.go-progress__state{font-size:14px;font-weight:700}.go-progress__state--done{color:#16a34a}.go-progress__sub{font-size:12px;color:var(--muted)}.go-bar{height:6px;border-radius:999px;background:var(--border);overflow:hidden}.go-bar__fill{display:block;height:100%;background:var(--primary);border-radius:999px;transition:width .25s ease}.go-list{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:4px 12px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:8px}.go-row{display:flex;align-items:flex-start;gap:10px;padding:12px;background:var(--surface);border:1px solid var(--border);border-radius:14px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-row:active{background:var(--primary-soft)}.go-row--not-needed{background:#f8fafc;border-color:#e2e8f0}.go-row--not-needed:active{background:#f1f5f9}.go-check{flex-shrink:0;width:30px;height:30px;margin-top:1px;padding:0;border:2px solid var(--border);border-radius:50%;background:var(--surface);color:#fff;font-size:14px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;position:relative;transition:border-color .15s,background .15s,transform .1s;-webkit-tap-highlight-color:transparent}.go-check:active{transform:scale(.9)}.go-check--none{border-color:var(--muted-2)}.go-check--partial{border-color:#f59e0b}.go-check--partial:before{content:\"\";position:absolute;inset:0;border-radius:50%;background:linear-gradient(to right,#f59e0b 50%,transparent 50%)}.go-check--partial i{position:relative;z-index:1}.go-check--done{border-color:var(--primary);background:var(--primary)}.go-check--skipped{border-color:var(--muted-2);background:#f1f5f9;color:var(--muted);cursor:default;opacity:.95}.go-row__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.go-row__title{font-size:15px;font-weight:700;color:var(--text);word-break:break-word}.go-row__title--done{color:var(--muted);text-decoration:line-through}.go-row__title--skipped{color:var(--muted-2);text-decoration:line-through;font-weight:600}.go-row__desc{font-size:13px;color:var(--muted);word-break:break-word}.go-row__note{display:flex;align-items:flex-start;gap:6px;margin-top:4px;font-size:12px;color:var(--primary);background:var(--primary-soft);border-radius:8px;padding:4px 8px}.go-row__note i{font-size:12px;margin-top:1px}.go-row__skip{flex-shrink:0;color:var(--muted-2)}.go-row__skip--on{color:#64748b;background:#e2e8f0}.go-row__skip:hover{background:#f1f5f9;color:var(--muted)}.go-row__skip--on:hover{background:#cbd5e1;color:var(--text)}.go-row__edit{flex-shrink:0}.go-row__danger{flex-shrink:0;color:var(--danger, #ef4444)}.go-row__danger:hover{background:#fee2e2;color:#ef4444}.go-empty{margin:24px 12px;text-align:center;font-size:14px;color:var(--muted)}.go-add-wrap{flex-shrink:0;padding:8px 12px calc(16px + env(safe-area-inset-bottom,0px))}.go-add{width:100%;border:1px dashed var(--primary);background:var(--primary-soft);color:var(--primary);border-radius:14px;padding:12px;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:16px;padding:18px;animation:go-sheet-in .2s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(12px);opacity:0}to{transform:translateY(0);opacity:1}}.go-confirm__title{margin:0 0 6px;font-size:17px;font-weight:700}.go-confirm__body{margin:0 0 16px;font-size:14px;color:var(--muted)}.go-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: GoingOutEditDialogComponent, selector: "app-going-out-edit-dialog", inputs: ["mode", "heading", "showStatus", "showNote", "canEditTitleDesc", "allowPartial", "initialTitle", "initialDesc", "initialNote", "initialStatus"], outputs: ["cancel", "save"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
22055
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: GoingOutChecklistComponent, isStandalone: true, selector: "app-going-out-checklist", inputs: { babyName: { classPropertyName: "babyName", publicName: "babyName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back", manage: "manage" }, ngImport: i0, template: "<section class=\"go-root\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"title()\">\n <header class=\"go-header\">\n <button type=\"button\" class=\"go-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"go-header__title\">{{ title() }}</h1>\n <span class=\"go-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"go-icon-btn\"\n (click)=\"refresh()\"\n [disabled]=\"sheetLoading()\"\n title=\"\u0110\u1ED3ng b\u1ED9 t\u1EEB Google Sheet\"\n aria-label=\"\u0110\u1ED3ng b\u1ED9 t\u1EEB Google Sheet\"\n >\n <i class=\"pi pi-cloud-download\" [class.go-icon-btn__spin]=\"sheetLoading()\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"go-header-menu\">\n <button\n type=\"button\"\n class=\"go-icon-btn\"\n (click)=\"toggleHeaderMenu()\"\n aria-label=\"Th\u00EAm thao t\u00E1c\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"headerMenuOpen()\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (headerMenuOpen()) {\n <div class=\"go-header-menu__panel\" role=\"menu\">\n <button type=\"button\" role=\"menuitem\" class=\"go-header-menu__item\" (click)=\"onManageFromMenu()\">\n <i class=\"pi pi-list\" aria-hidden=\"true\"></i>\n Qu\u1EA3n l\u00FD danh s\u00E1ch m\u1EABu\n </button>\n <button type=\"button\" role=\"menuitem\" class=\"go-header-menu__item\" (click)=\"onResetFromMenu()\">\n <i class=\"pi pi-undo\" aria-hidden=\"true\"></i>\n \u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\n </button>\n </div>\n }\n </div>\n </header>\n\n @if (headerMenuOpen()) {\n <button type=\"button\" class=\"go-header-menu__backdrop\" aria-label=\"\u0110\u00F3ng menu\" (click)=\"closeHeaderMenu()\"></button>\n }\n\n <div\n class=\"go-hero\"\n [class.go-hero--done]=\"allDone()\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div class=\"go-hero__copy\">\n <span class=\"go-hero__eyebrow\">Ra ngo\u00E0i</span>\n <span class=\"go-hero__title\">{{ heroTitle() }}</span>\n <span class=\"go-hero__sub\">\n {{ packedCount() }}/{{ activeTripCount() }} m\u00F3n s\u1EB5n s\u00E0ng\n @if (notNeededCount() > 0) {\n \u00B7 {{ notNeededCount() }} kh\u00F4ng mang\n }\n </span>\n </div>\n <div\n class=\"go-hero__ring\"\n [class.go-hero__ring--done]=\"allDone()\"\n [attr.aria-label]=\"'Ti\u1EBFn \u0111\u1ED9 ' + progressPercent() + ' ph\u1EA7n tr\u0103m'\"\n >\n {{ progressPercent() }}%\n </div>\n </div>\n\n <p class=\"go-list-hint\">Ch\u1EA1m d\u00F2ng \u0111\u1EC3 tick \u00B7 K\u00E9o <i class=\"pi pi-bars\" aria-hidden=\"true\"></i> ho\u1EB7c \u2191\u2193 \u0111\u1EC3 s\u1EAFp x\u1EBFp (ch\u1EC9 tr\u00EAn m\u00E1y)</p>\n\n <div class=\"go-list\">\n @for (item of items(); track item.id; let i = $index; let last = $last) {\n <div\n class=\"go-row\"\n [class.go-row--not-needed]=\"item.notNeeded\"\n [class.go-row--dragging]=\"draggingId() === item.id\"\n [class.go-row--drag-over]=\"dragOverId() === item.id\"\n (dragover)=\"onDragOver($event, item)\"\n (drop)=\"onDrop($event, item)\"\n >\n <div class=\"go-row__sort\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"go-sort-btn\"\n [disabled]=\"i === 0\"\n (click)=\"moveItem(i, -1, $event)\"\n aria-label=\"\u0110\u01B0a m\u00F3n l\u00EAn\"\n >\n <i class=\"pi pi-angle-up\" aria-hidden=\"true\"></i>\n </button>\n <span\n class=\"go-row__grip\"\n draggable=\"true\"\n title=\"K\u00E9o \u0111\u1EC3 \u0111\u1ED5i th\u1EE9 t\u1EF1\"\n aria-label=\"K\u00E9o \u0111\u1EC3 \u0111\u1ED5i th\u1EE9 t\u1EF1\"\n (dragstart)=\"onDragStart($event, item)\"\n (dragend)=\"onDragEnd()\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n </span>\n <button\n type=\"button\"\n class=\"go-sort-btn\"\n [disabled]=\"last\"\n (click)=\"moveItem(i, 1, $event)\"\n aria-label=\"\u0110\u01B0a m\u00F3n xu\u1ED1ng\"\n >\n <i class=\"pi pi-angle-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <button\n type=\"button\"\n class=\"go-check\"\n [class.go-check--none]=\"item.status === 'none' && !item.notNeeded\"\n [class.go-check--partial]=\"item.status === 'partial' && !item.notNeeded\"\n [class.go-check--done]=\"item.status === 'done' && !item.notNeeded\"\n [class.go-check--skipped]=\"item.notNeeded\"\n (click)=\"cycleStatus(item); $event.stopPropagation()\"\n [attr.aria-label]=\"item.notNeeded ? 'Kh\u00F4ng mang l\u1EA7n n\u00E0y' : statusLabel(item.status)\"\n [disabled]=\"item.notNeeded\"\n >\n @if (!item.notNeeded && item.status === 'done') {\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n }\n @if (!item.notNeeded && item.status === 'partial') {\n <span class=\"go-check__half\" aria-hidden=\"true\">\u00BD</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"go-row__open\"\n (click)=\"onMainTap(item, $event)\"\n [attr.aria-label]=\"item.notNeeded ? item.title : '\u0110\u1ED5i tr\u1EA1ng th\u00E1i ' + item.title\"\n >\n <span class=\"go-row__title-row\">\n <span\n class=\"go-row__title\"\n [class.go-row__title--done]=\"item.status === 'done' && !item.notNeeded\"\n [class.go-row__title--skipped]=\"item.notNeeded\"\n >{{ item.title }}</span>\n <span class=\"go-row__badge\">{{ itemStatusBadge(item) }}</span>\n </span>\n @if (item.desc) {\n <span class=\"go-row__desc\">{{ item.desc }}</span>\n }\n @if (item.note) {\n <span class=\"go-row__note\">{{ item.note }}</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__detail\"\n (click)=\"openDetail(item, $event)\"\n title=\"Xem chi ti\u1EBFt\"\n aria-label=\"Xem chi ti\u1EBFt\"\n >\n <i class=\"pi pi-angle-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n } @empty {\n <p class=\"go-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o.</p>\n }\n </div>\n\n @if (allowAdd()) {\n <div class=\"go-add-wrap\">\n <button type=\"button\" class=\"go-add\" (click)=\"openAdd()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n cho chuy\u1EBFn n\u00E0y</span>\n </button>\n </div>\n }\n</section>\n\n@if (isDialogOpen()) {\n <app-going-out-edit-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [showStatus]=\"true\"\n [showNote]=\"true\"\n [showNotNeeded]=\"true\"\n [canEditTitleDesc]=\"dialogCanEditTitleDesc()\"\n [allowPartial]=\"allowPartial()\"\n [showRemoveFromTrip]=\"dialogShowRemove()\"\n [initialTitle]=\"editingItem()?.title ?? ''\"\n [initialDesc]=\"editingItem()?.desc ?? ''\"\n [initialNote]=\"editingItem()?.note ?? ''\"\n [initialStatus]=\"editingItem()?.status ?? 'none'\"\n [initialNotNeeded]=\"editingItem()?.notNeeded ?? false\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSaveEdit($event)\"\n (removeFromTrip)=\"removeFromDialog()\"\n />\n}\n\n@if (resetConfirm()) {\n <div class=\"go-overlay\" (click)=\"cancelReset()\" role=\"presentation\">\n <div class=\"go-confirm\" role=\"alertdialog\" aria-modal=\"true\" aria-label=\"\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\" (click)=\"$event.stopPropagation()\">\n <h2 class=\"go-confirm__title\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh?</h2>\n <p class=\"go-confirm__body\">\n Checklist s\u1EBD tr\u1EDF l\u1EA1i nh\u01B0 danh s\u00E1ch m\u1EABu: b\u1ECF tick, xo\u00E1 ghi ch\u00FA, b\u1ECF \u0111\u00E1nh d\u1EA5u kh\u00F4ng c\u1EA7n, hi\u1EC7n l\u1EA1i m\u00F3n \u0111\u00E3 \u1EA9n v\u00E0 b\u1ECF m\u00F3n ch\u1EC9 th\u00EAm cho l\u1EA7n \u0111i n\u00E0y.\n Th\u1EE9 t\u1EF1 s\u1EAFp x\u1EBFp tay c\u0169ng \u0111\u01B0\u1EE3c \u0111\u1EB7t l\u1EA1i theo danh s\u00E1ch m\u1EABu. Danh s\u00E1ch m\u1EABu trong c\u00E0i \u0111\u1EB7t kh\u00F4ng thay \u0111\u1ED5i.\n </p>\n <div class=\"go-confirm__foot\">\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancelReset()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"confirmReset()\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh</button>\n </div>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--bg: #f2f3f7;--surface: #ffffff;--border: #e2e5ee;--primary: #5b6cff;--primary-soft: #eef0ff;--orange: #ea580c;--orange-soft: #fff0e2;--green: #16a34a;--green-soft: #e4fbec;--amber: #d97706;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:go-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes go-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.go-root{display:flex;flex-direction:column;flex:1;min-height:0}.go-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.go-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-back:hover{background:var(--primary-soft);color:var(--primary)}.go-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.go-header__spacer{flex:1}.go-header-menu{position:relative;flex-shrink:0}.go-header-menu__backdrop{position:fixed;inset:0;z-index:5;border:none;background:transparent;cursor:default;padding:0}.go-header-menu__panel{position:absolute;top:calc(100% + 4px);right:0;z-index:6;min-width:220px;padding:6px;background:var(--surface);border:1px solid var(--border);border-radius:12px}.go-header-menu__item{width:100%;display:flex;align-items:center;gap:8px;padding:10px;border:none;border-radius:8px;background:transparent;color:var(--text);font-size:13px;font-weight:600;text-align:left;cursor:pointer}.go-header-menu__item i{color:var(--muted);font-size:14px}.go-header-menu__item:hover{background:var(--primary-soft)}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover:not(:disabled){background:var(--primary-soft);color:var(--primary)}.go-icon-btn:disabled{opacity:.55;cursor:default}.go-icon-btn__spin{animation:go-refresh-spin .8s linear infinite}@keyframes go-refresh-spin{to{transform:rotate(360deg)}}.go-hero{flex-shrink:0;display:flex;align-items:center;gap:12px;margin:10px 12px;padding:14px;background:var(--orange-soft);border:1px solid var(--border);border-radius:16px}.go-hero--done{background:var(--green-soft)}.go-hero__copy{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.go-hero__eyebrow{font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--orange)}.go-hero--done .go-hero__eyebrow{color:var(--green)}.go-hero__title{font-size:17px;font-weight:800;letter-spacing:-.02em;line-height:1.15}.go-hero__sub{font-size:12px;color:var(--muted);line-height:1.35}.go-hero__ring{flex-shrink:0;width:56px;height:56px;border-radius:50%;border:4px solid var(--orange);background:var(--surface);display:grid;place-items:center;font-size:13px;font-weight:800;color:var(--orange)}.go-hero__ring--done{border-color:var(--green);color:var(--green)}.go-list-hint{margin:0 12px 6px;font-size:11px;color:var(--muted-2);line-height:1.35}.go-list-hint i{font-size:10px}.go-list{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:4px 12px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:8px}.go-row{display:flex;align-items:flex-start;gap:6px;padding:8px 8px 8px 4px;background:var(--surface);border:1px solid var(--border);border-radius:14px}.go-row--not-needed{opacity:.62}.go-row--dragging{opacity:.45}.go-row--drag-over{border-color:var(--primary);background:var(--primary-soft)}.go-row__sort{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:0;padding-top:2px}.go-sort-btn{width:26px;height:20px;padding:0;border:none;background:transparent;color:var(--muted-2);font-size:11px;cursor:pointer;border-radius:4px;-webkit-tap-highlight-color:transparent}.go-sort-btn:hover:not(:disabled){color:var(--primary);background:var(--primary-soft)}.go-sort-btn:disabled{opacity:.25;cursor:default}.go-row__grip{width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;color:var(--muted-2);font-size:12px;cursor:grab;touch-action:none;border-radius:6px}.go-row__grip:active{cursor:grabbing}.go-row__grip:hover{color:var(--muted);background:var(--bg)}.go-check{flex-shrink:0;width:30px;height:30px;margin-top:1px;padding:0;border:2px solid var(--border);border-radius:50%;background:var(--surface);color:#fff;font-size:14px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;position:relative;transition:border-color .15s,background .15s,transform .1s;-webkit-tap-highlight-color:transparent}.go-check:active{transform:scale(.9)}.go-check--none{border-color:var(--muted-2)}.go-check--partial{border-color:var(--amber);background:var(--orange-soft);color:var(--amber)}.go-check__half{font-size:13px;font-weight:800;line-height:1}.go-check--done{border-color:var(--primary);background:var(--primary)}.go-check--skipped{border-color:var(--muted-2);background:#f1f5f9;color:var(--muted);cursor:default;opacity:.95}.go-row__open{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;padding:4px 2px;border:none;background:transparent;text-align:left;cursor:pointer;color:inherit;border-radius:8px;-webkit-tap-highlight-color:transparent}.go-row__open:active{background:var(--primary-soft)}.go-row__title-row{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.go-row__badge{font-size:10px;font-weight:700;padding:2px 7px;border-radius:99px;background:var(--bg);color:var(--muted);border:1px solid var(--border)}.go-row__title{font-size:14px;font-weight:700;color:var(--text);word-break:break-word}.go-row__title--done{color:var(--muted);text-decoration:line-through}.go-row__title--skipped{color:var(--muted-2);text-decoration:line-through;font-weight:600}.go-row__desc{font-size:13px;color:var(--muted);word-break:break-word}.go-row__note{font-size:12px;color:var(--muted);line-height:1.35}.go-row__detail{flex-shrink:0;margin-top:4px;color:var(--muted);font-size:14px}.go-empty{margin:24px 12px;text-align:center;font-size:14px;color:var(--muted)}.go-add-wrap{flex-shrink:0;padding:8px 12px calc(16px + env(safe-area-inset-bottom,0px))}.go-add{width:100%;border:1px dashed var(--primary);background:var(--primary-soft);color:var(--primary);border-radius:14px;padding:12px;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:16px;padding:18px;animation:go-sheet-in .2s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(12px);opacity:0}to{transform:translateY(0);opacity:1}}.go-confirm__title{margin:0 0 6px;font-size:17px;font-weight:700}.go-confirm__body{margin:0 0 16px;font-size:14px;color:var(--muted)}.go-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: GoingOutEditDialogComponent, selector: "app-going-out-edit-dialog", inputs: ["mode", "heading", "showStatus", "showNote", "showNotNeeded", "canEditTitleDesc", "allowPartial", "initialTitle", "initialDesc", "initialNote", "initialStatus", "initialNotNeeded", "showRemoveFromTrip"], outputs: ["cancel", "save", "removeFromTrip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21714
22056
  }
21715
22057
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GoingOutChecklistComponent, decorators: [{
21716
22058
  type: Component,
21717
- args: [{ selector: 'app-going-out-checklist', standalone: true, imports: [CommonModule, GoingOutEditDialogComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"go-root\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"title()\">\n <header class=\"go-header\">\n <button type=\"button\" class=\"go-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"go-header__title\">{{ title() }}</h1>\n <span class=\"go-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"go-header-btn\"\n (click)=\"refresh()\"\n [disabled]=\"sheetLoading()\"\n title=\"L\u1EA5y checklist m\u1EDBi nh\u1EA5t t\u1EEB Google Sheet (\u0111\u1ED3ng b\u1ED9 v\u1EDBi ng\u01B0\u1EDDi kh\u00E1c)\"\n aria-label=\"\u0110\u1ED3ng b\u1ED9 t\u1EEB Google Sheet\"\n >\n <i class=\"pi pi-cloud-download\" [class.go-header-btn__spin]=\"sheetLoading()\" aria-hidden=\"true\"></i>\n <span>\u0110\u1ED3ng b\u1ED9</span>\n </button>\n <button\n type=\"button\"\n class=\"go-header-btn go-header-btn--muted\"\n (click)=\"askReset()\"\n title=\"\u0110\u1EB7t l\u1EA1i checklist: tick, ghi ch\u00FA, m\u00F3n \u0111\u00E3 \u1EA9n ho\u1EB7c th\u00EAm t\u1EA1m\"\n aria-label=\"\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\"\n >\n <i class=\"pi pi-undo\" aria-hidden=\"true\"></i>\n <span>\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh</span>\n </button>\n <button\n type=\"button\"\n class=\"go-icon-btn\"\n (click)=\"manage.emit()\"\n title=\"Qu\u1EA3n l\u00FD danh s\u00E1ch m\u00F3n m\u1EABu\"\n aria-label=\"Qu\u1EA3n l\u00FD danh s\u00E1ch\"\n >\n <i class=\"pi pi-list\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n @if (allDone()) {\n <div class=\"go-ready\" role=\"status\">\n <i class=\"pi pi-check-circle\" aria-hidden=\"true\"></i>\n <span>S\u1EB5n s\u00E0ng \u2014 mang \u0111\u1ED3 \u0111i th\u00F4i</span>\n </div>\n }\n\n <div class=\"go-progress\">\n <div class=\"go-progress__row\">\n <span class=\"go-progress__count\">{{ doneCount() }}/{{ total() }}</span>\n <div class=\"go-progress__meta\">\n <span class=\"go-progress__state\" [class.go-progress__state--done]=\"allDone()\">\n {{ allDone() ? 'S\u1EB5n s\u00E0ng \u0111i r\u1ED3i!' : '\u0110ang chu\u1EA9n b\u1ECB\u2026' }}\n </span>\n <span class=\"go-progress__sub\">\n {{ readyCount() }} \u0111ang l\u00E0m \u00B7 {{ total() - readyCount() }} ch\u01B0a b\u1EAFt \u0111\u1EA7u\n @if (notNeededCount() > 0) {\n \u00B7 {{ notNeededCount() }} kh\u00F4ng c\u1EA7n\n }\n </span>\n </div>\n </div>\n <div class=\"go-bar\"><span class=\"go-bar__fill\" [style.width.%]=\"total() ? (doneCount() / total()) * 100 : 0\"></span></div>\n </div>\n\n <div class=\"go-list\">\n @for (item of items(); track item.id) {\n <div\n class=\"go-row\"\n [class.go-row--not-needed]=\"item.notNeeded\"\n (click)=\"cycleStatus(item)\"\n >\n <button\n type=\"button\"\n class=\"go-check\"\n [class.go-check--none]=\"item.status === 'none' && !item.notNeeded\"\n [class.go-check--partial]=\"item.status === 'partial'\"\n [class.go-check--done]=\"item.status === 'done'\"\n [class.go-check--skipped]=\"item.notNeeded\"\n (click)=\"cycleStatus(item); $event.stopPropagation()\"\n [attr.aria-label]=\"item.notNeeded ? 'Kh\u00F4ng c\u1EA7n l\u1EA7n n\u00E0y' : statusLabel(item.status)\"\n [attr.aria-pressed]=\"item.notNeeded || item.status !== 'none'\"\n [disabled]=\"item.notNeeded\"\n >\n @if (!item.notNeeded && item.status === 'done') {\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n }\n </button>\n\n <div class=\"go-row__body\" (click)=\"cycleStatus(item); $event.stopPropagation()\">\n <span\n class=\"go-row__title\"\n [class.go-row__title--done]=\"item.status === 'done'\"\n [class.go-row__title--skipped]=\"item.notNeeded\"\n >{{ item.title }}</span>\n @if (item.desc) {\n <span class=\"go-row__desc\">{{ item.desc }}</span>\n }\n @if (item.note) {\n <span class=\"go-row__note\">\n <i class=\"pi pi-file-edit\" aria-hidden=\"true\"></i>\n {{ item.note }}\n </span>\n }\n </div>\n\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__skip\"\n [class.go-row__skip--on]=\"item.notNeeded\"\n (click)=\"toggleNotNeeded(item, $event)\"\n [title]=\"item.notNeeded ? 'C\u1EA7n mang l\u1EA1i l\u1EA7n n\u00E0y' : 'Kh\u00F4ng c\u1EA7n mang l\u1EA7n n\u00E0y'\"\n [attr.aria-label]=\"item.notNeeded ? 'C\u1EA7n mang l\u1EA1i l\u1EA7n n\u00E0y' : 'Kh\u00F4ng c\u1EA7n mang l\u1EA7n n\u00E0y'\"\n [attr.aria-pressed]=\"item.notNeeded\"\n >\n <i class=\"pi pi-ban\" aria-hidden=\"true\"></i>\n </button>\n\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__edit\"\n (click)=\"openEdit(item); $event.stopPropagation()\"\n title=\"Ghi ch\u00FA / s\u1EEDa\"\n aria-label=\"Ghi ch\u00FA / s\u1EEDa\"\n >\n <i class=\"pi pi-pencil\" aria-hidden=\"true\"></i>\n </button>\n\n @if (allowEdit()) {\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__danger\"\n (click)=\"removeItem(item, $event)\"\n title=\"B\u1ECF kh\u1ECFi checklist l\u1EA7n n\u00E0y\"\n aria-label=\"B\u1ECF kh\u1ECFi checklist l\u1EA7n n\u00E0y\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n } @empty {\n <p class=\"go-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o.</p>\n }\n </div>\n\n @if (allowAdd()) {\n <div class=\"go-add-wrap\">\n <button type=\"button\" class=\"go-add\" (click)=\"openAdd()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n }\n</section>\n\n@if (isDialogOpen()) {\n <app-going-out-edit-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [showStatus]=\"true\"\n [showNote]=\"true\"\n [canEditTitleDesc]=\"dialogCanEditTitleDesc()\"\n [allowPartial]=\"allowPartial()\"\n [initialTitle]=\"editingItem()?.title ?? ''\"\n [initialDesc]=\"editingItem()?.desc ?? ''\"\n [initialNote]=\"editingItem()?.note ?? ''\"\n [initialStatus]=\"editingItem()?.status ?? 'none'\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSaveEdit($event)\"\n />\n}\n\n@if (resetConfirm()) {\n <div class=\"go-overlay\" (click)=\"cancelReset()\" role=\"presentation\">\n <div class=\"go-confirm\" role=\"alertdialog\" aria-modal=\"true\" aria-label=\"\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\" (click)=\"$event.stopPropagation()\">\n <h2 class=\"go-confirm__title\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh?</h2>\n <p class=\"go-confirm__body\">\n Checklist s\u1EBD tr\u1EDF l\u1EA1i nh\u01B0 danh s\u00E1ch m\u1EABu: b\u1ECF tick, xo\u00E1 ghi ch\u00FA, b\u1ECF \u0111\u00E1nh d\u1EA5u kh\u00F4ng c\u1EA7n, hi\u1EC7n l\u1EA1i m\u00F3n \u0111\u00E3 \u1EA9n v\u00E0 b\u1ECF m\u00F3n ch\u1EC9 th\u00EAm cho l\u1EA7n \u0111i n\u00E0y.\n Danh s\u00E1ch m\u1EABu trong c\u00E0i \u0111\u1EB7t kh\u00F4ng thay \u0111\u1ED5i.\n </p>\n <div class=\"go-confirm__foot\">\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancelReset()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"confirmReset()\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh</button>\n </div>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:go-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes go-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.go-root{display:flex;flex-direction:column;flex:1;min-height:0}.go-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.go-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-back:hover{background:var(--primary-soft);color:var(--primary)}.go-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.go-header__spacer{flex:1}.go-header-btn{flex-shrink:0;display:inline-flex;align-items:center;gap:4px;height:30px;padding:0 8px;border:1px solid var(--border);border-radius:8px;background:var(--surface);color:var(--primary);font-size:11px;font-weight:700;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-header-btn i{font-size:12px}.go-header-btn:hover:not(:disabled){background:var(--primary-soft);border-color:color-mix(in srgb,var(--primary) 35%,var(--border))}.go-header-btn:disabled{opacity:.55;cursor:default}.go-header-btn--muted{color:var(--muted)}.go-header-btn--muted:hover:not(:disabled){background:#f1f5f9;border-color:var(--border);color:var(--text)}.go-header-btn__spin{animation:go-refresh-spin .8s linear infinite}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover:not(:disabled){background:var(--primary-soft);color:var(--primary)}.go-icon-btn:disabled{opacity:.55;cursor:default}.go-icon-btn__spin{animation:go-refresh-spin .8s linear infinite}@keyframes go-refresh-spin{to{transform:rotate(360deg)}}.go-ready{display:flex;align-items:center;gap:8px;margin:12px;padding:8px 14px;background:#e9f9ef;border:1px solid #b6e7c4;border-radius:12px;color:#15803d;font-size:13px;font-weight:600}.go-ready i{font-size:16px}.go-progress{margin:0 12px 10px;padding:12px 14px;background:var(--surface);border:1px solid var(--border);border-radius:14px}.go-progress__row{display:flex;align-items:center;gap:12px;margin-bottom:10px}.go-progress__count{font-size:22px;font-weight:800;color:var(--text);line-height:1}.go-progress__meta{display:flex;flex-direction:column;gap:2px}.go-progress__state{font-size:14px;font-weight:700}.go-progress__state--done{color:#16a34a}.go-progress__sub{font-size:12px;color:var(--muted)}.go-bar{height:6px;border-radius:999px;background:var(--border);overflow:hidden}.go-bar__fill{display:block;height:100%;background:var(--primary);border-radius:999px;transition:width .25s ease}.go-list{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:4px 12px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:8px}.go-row{display:flex;align-items:flex-start;gap:10px;padding:12px;background:var(--surface);border:1px solid var(--border);border-radius:14px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-row:active{background:var(--primary-soft)}.go-row--not-needed{background:#f8fafc;border-color:#e2e8f0}.go-row--not-needed:active{background:#f1f5f9}.go-check{flex-shrink:0;width:30px;height:30px;margin-top:1px;padding:0;border:2px solid var(--border);border-radius:50%;background:var(--surface);color:#fff;font-size:14px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;position:relative;transition:border-color .15s,background .15s,transform .1s;-webkit-tap-highlight-color:transparent}.go-check:active{transform:scale(.9)}.go-check--none{border-color:var(--muted-2)}.go-check--partial{border-color:#f59e0b}.go-check--partial:before{content:\"\";position:absolute;inset:0;border-radius:50%;background:linear-gradient(to right,#f59e0b 50%,transparent 50%)}.go-check--partial i{position:relative;z-index:1}.go-check--done{border-color:var(--primary);background:var(--primary)}.go-check--skipped{border-color:var(--muted-2);background:#f1f5f9;color:var(--muted);cursor:default;opacity:.95}.go-row__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.go-row__title{font-size:15px;font-weight:700;color:var(--text);word-break:break-word}.go-row__title--done{color:var(--muted);text-decoration:line-through}.go-row__title--skipped{color:var(--muted-2);text-decoration:line-through;font-weight:600}.go-row__desc{font-size:13px;color:var(--muted);word-break:break-word}.go-row__note{display:flex;align-items:flex-start;gap:6px;margin-top:4px;font-size:12px;color:var(--primary);background:var(--primary-soft);border-radius:8px;padding:4px 8px}.go-row__note i{font-size:12px;margin-top:1px}.go-row__skip{flex-shrink:0;color:var(--muted-2)}.go-row__skip--on{color:#64748b;background:#e2e8f0}.go-row__skip:hover{background:#f1f5f9;color:var(--muted)}.go-row__skip--on:hover{background:#cbd5e1;color:var(--text)}.go-row__edit{flex-shrink:0}.go-row__danger{flex-shrink:0;color:var(--danger, #ef4444)}.go-row__danger:hover{background:#fee2e2;color:#ef4444}.go-empty{margin:24px 12px;text-align:center;font-size:14px;color:var(--muted)}.go-add-wrap{flex-shrink:0;padding:8px 12px calc(16px + env(safe-area-inset-bottom,0px))}.go-add{width:100%;border:1px dashed var(--primary);background:var(--primary-soft);color:var(--primary);border-radius:14px;padding:12px;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:16px;padding:18px;animation:go-sheet-in .2s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(12px);opacity:0}to{transform:translateY(0);opacity:1}}.go-confirm__title{margin:0 0 6px;font-size:17px;font-weight:700}.go-confirm__body{margin:0 0 16px;font-size:14px;color:var(--muted)}.go-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}\n"] }]
22059
+ args: [{ selector: 'app-going-out-checklist', standalone: true, imports: [CommonModule, GoingOutEditDialogComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"go-root\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"title()\">\n <header class=\"go-header\">\n <button type=\"button\" class=\"go-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"go-header__title\">{{ title() }}</h1>\n <span class=\"go-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"go-icon-btn\"\n (click)=\"refresh()\"\n [disabled]=\"sheetLoading()\"\n title=\"\u0110\u1ED3ng b\u1ED9 t\u1EEB Google Sheet\"\n aria-label=\"\u0110\u1ED3ng b\u1ED9 t\u1EEB Google Sheet\"\n >\n <i class=\"pi pi-cloud-download\" [class.go-icon-btn__spin]=\"sheetLoading()\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"go-header-menu\">\n <button\n type=\"button\"\n class=\"go-icon-btn\"\n (click)=\"toggleHeaderMenu()\"\n aria-label=\"Th\u00EAm thao t\u00E1c\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"headerMenuOpen()\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (headerMenuOpen()) {\n <div class=\"go-header-menu__panel\" role=\"menu\">\n <button type=\"button\" role=\"menuitem\" class=\"go-header-menu__item\" (click)=\"onManageFromMenu()\">\n <i class=\"pi pi-list\" aria-hidden=\"true\"></i>\n Qu\u1EA3n l\u00FD danh s\u00E1ch m\u1EABu\n </button>\n <button type=\"button\" role=\"menuitem\" class=\"go-header-menu__item\" (click)=\"onResetFromMenu()\">\n <i class=\"pi pi-undo\" aria-hidden=\"true\"></i>\n \u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\n </button>\n </div>\n }\n </div>\n </header>\n\n @if (headerMenuOpen()) {\n <button type=\"button\" class=\"go-header-menu__backdrop\" aria-label=\"\u0110\u00F3ng menu\" (click)=\"closeHeaderMenu()\"></button>\n }\n\n <div\n class=\"go-hero\"\n [class.go-hero--done]=\"allDone()\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div class=\"go-hero__copy\">\n <span class=\"go-hero__eyebrow\">Ra ngo\u00E0i</span>\n <span class=\"go-hero__title\">{{ heroTitle() }}</span>\n <span class=\"go-hero__sub\">\n {{ packedCount() }}/{{ activeTripCount() }} m\u00F3n s\u1EB5n s\u00E0ng\n @if (notNeededCount() > 0) {\n \u00B7 {{ notNeededCount() }} kh\u00F4ng mang\n }\n </span>\n </div>\n <div\n class=\"go-hero__ring\"\n [class.go-hero__ring--done]=\"allDone()\"\n [attr.aria-label]=\"'Ti\u1EBFn \u0111\u1ED9 ' + progressPercent() + ' ph\u1EA7n tr\u0103m'\"\n >\n {{ progressPercent() }}%\n </div>\n </div>\n\n <p class=\"go-list-hint\">Ch\u1EA1m d\u00F2ng \u0111\u1EC3 tick \u00B7 K\u00E9o <i class=\"pi pi-bars\" aria-hidden=\"true\"></i> ho\u1EB7c \u2191\u2193 \u0111\u1EC3 s\u1EAFp x\u1EBFp (ch\u1EC9 tr\u00EAn m\u00E1y)</p>\n\n <div class=\"go-list\">\n @for (item of items(); track item.id; let i = $index; let last = $last) {\n <div\n class=\"go-row\"\n [class.go-row--not-needed]=\"item.notNeeded\"\n [class.go-row--dragging]=\"draggingId() === item.id\"\n [class.go-row--drag-over]=\"dragOverId() === item.id\"\n (dragover)=\"onDragOver($event, item)\"\n (drop)=\"onDrop($event, item)\"\n >\n <div class=\"go-row__sort\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"go-sort-btn\"\n [disabled]=\"i === 0\"\n (click)=\"moveItem(i, -1, $event)\"\n aria-label=\"\u0110\u01B0a m\u00F3n l\u00EAn\"\n >\n <i class=\"pi pi-angle-up\" aria-hidden=\"true\"></i>\n </button>\n <span\n class=\"go-row__grip\"\n draggable=\"true\"\n title=\"K\u00E9o \u0111\u1EC3 \u0111\u1ED5i th\u1EE9 t\u1EF1\"\n aria-label=\"K\u00E9o \u0111\u1EC3 \u0111\u1ED5i th\u1EE9 t\u1EF1\"\n (dragstart)=\"onDragStart($event, item)\"\n (dragend)=\"onDragEnd()\"\n >\n <i class=\"pi pi-bars\" aria-hidden=\"true\"></i>\n </span>\n <button\n type=\"button\"\n class=\"go-sort-btn\"\n [disabled]=\"last\"\n (click)=\"moveItem(i, 1, $event)\"\n aria-label=\"\u0110\u01B0a m\u00F3n xu\u1ED1ng\"\n >\n <i class=\"pi pi-angle-down\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <button\n type=\"button\"\n class=\"go-check\"\n [class.go-check--none]=\"item.status === 'none' && !item.notNeeded\"\n [class.go-check--partial]=\"item.status === 'partial' && !item.notNeeded\"\n [class.go-check--done]=\"item.status === 'done' && !item.notNeeded\"\n [class.go-check--skipped]=\"item.notNeeded\"\n (click)=\"cycleStatus(item); $event.stopPropagation()\"\n [attr.aria-label]=\"item.notNeeded ? 'Kh\u00F4ng mang l\u1EA7n n\u00E0y' : statusLabel(item.status)\"\n [disabled]=\"item.notNeeded\"\n >\n @if (!item.notNeeded && item.status === 'done') {\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n }\n @if (!item.notNeeded && item.status === 'partial') {\n <span class=\"go-check__half\" aria-hidden=\"true\">\u00BD</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"go-row__open\"\n (click)=\"onMainTap(item, $event)\"\n [attr.aria-label]=\"item.notNeeded ? item.title : '\u0110\u1ED5i tr\u1EA1ng th\u00E1i ' + item.title\"\n >\n <span class=\"go-row__title-row\">\n <span\n class=\"go-row__title\"\n [class.go-row__title--done]=\"item.status === 'done' && !item.notNeeded\"\n [class.go-row__title--skipped]=\"item.notNeeded\"\n >{{ item.title }}</span>\n <span class=\"go-row__badge\">{{ itemStatusBadge(item) }}</span>\n </span>\n @if (item.desc) {\n <span class=\"go-row__desc\">{{ item.desc }}</span>\n }\n @if (item.note) {\n <span class=\"go-row__note\">{{ item.note }}</span>\n }\n </button>\n\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__detail\"\n (click)=\"openDetail(item, $event)\"\n title=\"Xem chi ti\u1EBFt\"\n aria-label=\"Xem chi ti\u1EBFt\"\n >\n <i class=\"pi pi-angle-right\" aria-hidden=\"true\"></i>\n </button>\n </div>\n } @empty {\n <p class=\"go-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o.</p>\n }\n </div>\n\n @if (allowAdd()) {\n <div class=\"go-add-wrap\">\n <button type=\"button\" class=\"go-add\" (click)=\"openAdd()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n cho chuy\u1EBFn n\u00E0y</span>\n </button>\n </div>\n }\n</section>\n\n@if (isDialogOpen()) {\n <app-going-out-edit-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [showStatus]=\"true\"\n [showNote]=\"true\"\n [showNotNeeded]=\"true\"\n [canEditTitleDesc]=\"dialogCanEditTitleDesc()\"\n [allowPartial]=\"allowPartial()\"\n [showRemoveFromTrip]=\"dialogShowRemove()\"\n [initialTitle]=\"editingItem()?.title ?? ''\"\n [initialDesc]=\"editingItem()?.desc ?? ''\"\n [initialNote]=\"editingItem()?.note ?? ''\"\n [initialStatus]=\"editingItem()?.status ?? 'none'\"\n [initialNotNeeded]=\"editingItem()?.notNeeded ?? false\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSaveEdit($event)\"\n (removeFromTrip)=\"removeFromDialog()\"\n />\n}\n\n@if (resetConfirm()) {\n <div class=\"go-overlay\" (click)=\"cancelReset()\" role=\"presentation\">\n <div class=\"go-confirm\" role=\"alertdialog\" aria-modal=\"true\" aria-label=\"\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh\" (click)=\"$event.stopPropagation()\">\n <h2 class=\"go-confirm__title\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh?</h2>\n <p class=\"go-confirm__body\">\n Checklist s\u1EBD tr\u1EDF l\u1EA1i nh\u01B0 danh s\u00E1ch m\u1EABu: b\u1ECF tick, xo\u00E1 ghi ch\u00FA, b\u1ECF \u0111\u00E1nh d\u1EA5u kh\u00F4ng c\u1EA7n, hi\u1EC7n l\u1EA1i m\u00F3n \u0111\u00E3 \u1EA9n v\u00E0 b\u1ECF m\u00F3n ch\u1EC9 th\u00EAm cho l\u1EA7n \u0111i n\u00E0y.\n Th\u1EE9 t\u1EF1 s\u1EAFp x\u1EBFp tay c\u0169ng \u0111\u01B0\u1EE3c \u0111\u1EB7t l\u1EA1i theo danh s\u00E1ch m\u1EABu. Danh s\u00E1ch m\u1EABu trong c\u00E0i \u0111\u1EB7t kh\u00F4ng thay \u0111\u1ED5i.\n </p>\n <div class=\"go-confirm__foot\">\n <button type=\"button\" class=\"go-btn go-btn--ghost\" (click)=\"cancelReset()\">Hu\u1EF7</button>\n <button type=\"button\" class=\"go-btn go-btn--primary\" (click)=\"confirmReset()\">\u0110\u1EB7t l\u1EA1i m\u1EB7c \u0111\u1ECBnh</button>\n </div>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{--bg: #f2f3f7;--surface: #ffffff;--border: #e2e5ee;--primary: #5b6cff;--primary-soft: #eef0ff;--orange: #ea580c;--orange-soft: #fff0e2;--green: #16a34a;--green-soft: #e4fbec;--amber: #d97706;--text: #0f172a;--muted: #64748b;--muted-2: #94a3b8;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:go-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes go-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.go-root{display:flex;flex-direction:column;flex:1;min-height:0}.go-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.go-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-back:hover{background:var(--primary-soft);color:var(--primary)}.go-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.go-header__spacer{flex:1}.go-header-menu{position:relative;flex-shrink:0}.go-header-menu__backdrop{position:fixed;inset:0;z-index:5;border:none;background:transparent;cursor:default;padding:0}.go-header-menu__panel{position:absolute;top:calc(100% + 4px);right:0;z-index:6;min-width:220px;padding:6px;background:var(--surface);border:1px solid var(--border);border-radius:12px}.go-header-menu__item{width:100%;display:flex;align-items:center;gap:8px;padding:10px;border:none;border-radius:8px;background:transparent;color:var(--text);font-size:13px;font-weight:600;text-align:left;cursor:pointer}.go-header-menu__item i{color:var(--muted);font-size:14px}.go-header-menu__item:hover{background:var(--primary-soft)}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover:not(:disabled){background:var(--primary-soft);color:var(--primary)}.go-icon-btn:disabled{opacity:.55;cursor:default}.go-icon-btn__spin{animation:go-refresh-spin .8s linear infinite}@keyframes go-refresh-spin{to{transform:rotate(360deg)}}.go-hero{flex-shrink:0;display:flex;align-items:center;gap:12px;margin:10px 12px;padding:14px;background:var(--orange-soft);border:1px solid var(--border);border-radius:16px}.go-hero--done{background:var(--green-soft)}.go-hero__copy{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}.go-hero__eyebrow{font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--orange)}.go-hero--done .go-hero__eyebrow{color:var(--green)}.go-hero__title{font-size:17px;font-weight:800;letter-spacing:-.02em;line-height:1.15}.go-hero__sub{font-size:12px;color:var(--muted);line-height:1.35}.go-hero__ring{flex-shrink:0;width:56px;height:56px;border-radius:50%;border:4px solid var(--orange);background:var(--surface);display:grid;place-items:center;font-size:13px;font-weight:800;color:var(--orange)}.go-hero__ring--done{border-color:var(--green);color:var(--green)}.go-list-hint{margin:0 12px 6px;font-size:11px;color:var(--muted-2);line-height:1.35}.go-list-hint i{font-size:10px}.go-list{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:4px 12px calc(16px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:8px}.go-row{display:flex;align-items:flex-start;gap:6px;padding:8px 8px 8px 4px;background:var(--surface);border:1px solid var(--border);border-radius:14px}.go-row--not-needed{opacity:.62}.go-row--dragging{opacity:.45}.go-row--drag-over{border-color:var(--primary);background:var(--primary-soft)}.go-row__sort{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:0;padding-top:2px}.go-sort-btn{width:26px;height:20px;padding:0;border:none;background:transparent;color:var(--muted-2);font-size:11px;cursor:pointer;border-radius:4px;-webkit-tap-highlight-color:transparent}.go-sort-btn:hover:not(:disabled){color:var(--primary);background:var(--primary-soft)}.go-sort-btn:disabled{opacity:.25;cursor:default}.go-row__grip{width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;color:var(--muted-2);font-size:12px;cursor:grab;touch-action:none;border-radius:6px}.go-row__grip:active{cursor:grabbing}.go-row__grip:hover{color:var(--muted);background:var(--bg)}.go-check{flex-shrink:0;width:30px;height:30px;margin-top:1px;padding:0;border:2px solid var(--border);border-radius:50%;background:var(--surface);color:#fff;font-size:14px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;position:relative;transition:border-color .15s,background .15s,transform .1s;-webkit-tap-highlight-color:transparent}.go-check:active{transform:scale(.9)}.go-check--none{border-color:var(--muted-2)}.go-check--partial{border-color:var(--amber);background:var(--orange-soft);color:var(--amber)}.go-check__half{font-size:13px;font-weight:800;line-height:1}.go-check--done{border-color:var(--primary);background:var(--primary)}.go-check--skipped{border-color:var(--muted-2);background:#f1f5f9;color:var(--muted);cursor:default;opacity:.95}.go-row__open{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;padding:4px 2px;border:none;background:transparent;text-align:left;cursor:pointer;color:inherit;border-radius:8px;-webkit-tap-highlight-color:transparent}.go-row__open:active{background:var(--primary-soft)}.go-row__title-row{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.go-row__badge{font-size:10px;font-weight:700;padding:2px 7px;border-radius:99px;background:var(--bg);color:var(--muted);border:1px solid var(--border)}.go-row__title{font-size:14px;font-weight:700;color:var(--text);word-break:break-word}.go-row__title--done{color:var(--muted);text-decoration:line-through}.go-row__title--skipped{color:var(--muted-2);text-decoration:line-through;font-weight:600}.go-row__desc{font-size:13px;color:var(--muted);word-break:break-word}.go-row__note{font-size:12px;color:var(--muted);line-height:1.35}.go-row__detail{flex-shrink:0;margin-top:4px;color:var(--muted);font-size:14px}.go-empty{margin:24px 12px;text-align:center;font-size:14px;color:var(--muted)}.go-add-wrap{flex-shrink:0;padding:8px 12px calc(16px + env(safe-area-inset-bottom,0px))}.go-add{width:100%;border:1px dashed var(--primary);background:var(--primary-soft);color:var(--primary);border-radius:14px;padding:12px;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;animation:go-fade .18s ease-out}@keyframes go-fade{0%{opacity:0}to{opacity:1}}.go-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:16px;padding:18px;animation:go-sheet-in .2s cubic-bezier(.22,1,.36,1)}@keyframes go-sheet-in{0%{transform:translateY(12px);opacity:0}to{transform:translateY(0);opacity:1}}.go-confirm__title{margin:0 0 6px;font-size:17px;font-weight:700}.go-confirm__body{margin:0 0 16px;font-size:14px;color:var(--muted)}.go-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.go-btn{border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-btn--ghost{background:transparent;color:var(--muted)}.go-btn--primary{background:var(--primary);color:#fff}\n"] }]
21718
22060
  }], ctorParameters: () => [], propDecorators: { babyName: [{ type: i0.Input, args: [{ isSignal: true, alias: "babyName", required: false }] }], back: [{ type: i0.Output, args: ["back"] }], manage: [{ type: i0.Output, args: ["manage"] }] } });
21719
22061
 
21720
22062
  class GoingOutListComponent {
@@ -21772,7 +22114,7 @@ class GoingOutListComponent {
21772
22114
  this.svc.setAllowPartial(v);
21773
22115
  }
21774
22116
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GoingOutListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21775
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: GoingOutListComponent, isStandalone: true, selector: "app-going-out-list", outputs: { back: "back" }, ngImport: i0, template: "<section class=\"go-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Danh s\u00E1ch m\u00F3n\">\n <header class=\"go-header\">\n <button type=\"button\" class=\"go-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"go-header__title\">C\u00E0i \u0111\u1EB7t ra ngo\u00E0i</h1>\n <span class=\"go-header__spacer\"></span>\n </header>\n\n <!-- Quy\u1EC1n -->\n <div class=\"go-perms\">\n <label class=\"go-toggle\">\n <span class=\"go-toggle__text\">\n <span class=\"go-toggle__title\">Cho ph\u00E9p s\u1EEDa &amp; xo\u00E1 m\u00F3n</span>\n <span class=\"go-toggle__sub\">\u00C1p d\u1EE5ng cho trang checklist (kh\u00F4ng kho\u00E1 trang n\u00E0y)</span>\n </span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"allowEdit()\"\n (change)=\"setAllowEdit($any($event.target).checked)\"\n />\n </label>\n <label class=\"go-toggle\">\n <span class=\"go-toggle__text\">\n <span class=\"go-toggle__title\">Cho ph\u00E9p th\u00EAm m\u00F3n</span>\n <span class=\"go-toggle__sub\">Hi\u1EC7n n\u00FAt \"Th\u00EAm m\u00F3n\" \u1EDF trang checklist</span>\n </span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"allowAdd()\"\n (change)=\"setAllowAdd($any($event.target).checked)\"\n />\n </label>\n <label class=\"go-toggle\">\n <span class=\"go-toggle__text\">\n <span class=\"go-toggle__title\">Cho ph\u00E9p tr\u1EA1ng th\u00E1i \"1 ph\u1EA7n\"</span>\n <span class=\"go-toggle__sub\">T\u1EAFt \u0111\u1EC3 checkbox ch\u1EC9 c\u00F2n ch\u01B0a / xong</span>\n </span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"allowPartial()\"\n (change)=\"setAllowPartial($any($event.target).checked)\"\n />\n </label>\n </div>\n\n <div class=\"go-list\">\n @for (item of items(); track item.id) {\n <div class=\"go-row\" (click)=\"openEdit(item)\">\n <div class=\"go-row__body\">\n <span class=\"go-row__title\">{{ item.title }}</span>\n @if (item.desc) {\n <span class=\"go-row__desc\">{{ item.desc }}</span>\n }\n </div>\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__danger\"\n (click)=\"removeItem(item, $event)\"\n title=\"Xo\u00E1 m\u00F3n\"\n aria-label=\"Xo\u00E1 m\u00F3n\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n } @empty {\n <p class=\"go-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o. B\u1EA5m \u201CTh\u00EAm m\u00F3n\u201D \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u.</p>\n }\n </div>\n\n <div class=\"go-add-wrap\">\n <button type=\"button\" class=\"go-add\" (click)=\"openAdd()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n</section>\n\n@if (isDialogOpen()) {\n <app-going-out-edit-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [showStatus]=\"false\"\n [showNote]=\"false\"\n [canEditTitleDesc]=\"true\"\n [initialTitle]=\"draftTitle()\"\n [initialDesc]=\"draftDesc()\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSave($event)\"\n />\n}\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;--danger: #ef4444;--danger-soft: #fee2e2;position:fixed;inset:0;z-index:210;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:go-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes go-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.go-root{display:flex;flex-direction:column;flex:1;min-height:0}.go-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.go-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-back:hover{background:var(--primary-soft);color:var(--primary)}.go-header__title{margin:0;font-size:16px;font-weight:700}.go-header__spacer{flex:1}.go-perms{display:flex;flex-direction:column;gap:8px;padding:12px}.go-toggle{display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--surface);border:1px solid var(--border);border-radius:14px}.go-toggle__text{flex:1;display:flex;flex-direction:column;gap:2px}.go-toggle__title{font-size:14px;font-weight:700;color:var(--text)}.go-toggle__sub{font-size:12px;color:var(--muted)}.go-toggle input[type=checkbox]{width:40px;height:24px;flex-shrink:0;accent-color:var(--primary)}.go-list{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:0 12px 12px;display:flex;flex-direction:column;gap:8px}.go-row{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--surface);border:1px solid var(--border);border-radius:14px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-row:active{background:var(--primary-soft)}.go-row__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.go-row__title{font-size:15px;font-weight:700;color:var(--text);word-break:break-word}.go-row__desc{font-size:13px;color:var(--muted);word-break:break-word}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.go-row__danger{color:var(--danger)}.go-row__danger:hover{background:var(--danger-soft);color:var(--danger)}.go-empty{margin:24px 12px;text-align:center;font-size:14px;color:var(--muted)}.go-add-wrap{flex-shrink:0;padding:8px 12px calc(16px + env(safe-area-inset-bottom,0px))}.go-add{width:100%;border:1px dashed var(--primary);background:var(--primary-soft);color:var(--primary);border-radius:14px;padding:12px;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;-webkit-tap-highlight-color:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: GoingOutEditDialogComponent, selector: "app-going-out-edit-dialog", inputs: ["mode", "heading", "showStatus", "showNote", "canEditTitleDesc", "allowPartial", "initialTitle", "initialDesc", "initialNote", "initialStatus"], outputs: ["cancel", "save"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
22117
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: GoingOutListComponent, isStandalone: true, selector: "app-going-out-list", outputs: { back: "back" }, ngImport: i0, template: "<section class=\"go-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Danh s\u00E1ch m\u00F3n\">\n <header class=\"go-header\">\n <button type=\"button\" class=\"go-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"go-header__title\">C\u00E0i \u0111\u1EB7t ra ngo\u00E0i</h1>\n <span class=\"go-header__spacer\"></span>\n </header>\n\n <!-- Quy\u1EC1n -->\n <div class=\"go-perms\">\n <label class=\"go-toggle\">\n <span class=\"go-toggle__text\">\n <span class=\"go-toggle__title\">Cho ph\u00E9p s\u1EEDa &amp; xo\u00E1 m\u00F3n</span>\n <span class=\"go-toggle__sub\">\u00C1p d\u1EE5ng cho trang checklist (kh\u00F4ng kho\u00E1 trang n\u00E0y)</span>\n </span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"allowEdit()\"\n (change)=\"setAllowEdit($any($event.target).checked)\"\n />\n </label>\n <label class=\"go-toggle\">\n <span class=\"go-toggle__text\">\n <span class=\"go-toggle__title\">Cho ph\u00E9p th\u00EAm m\u00F3n</span>\n <span class=\"go-toggle__sub\">Hi\u1EC7n n\u00FAt \"Th\u00EAm m\u00F3n\" \u1EDF trang checklist</span>\n </span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"allowAdd()\"\n (change)=\"setAllowAdd($any($event.target).checked)\"\n />\n </label>\n <label class=\"go-toggle\">\n <span class=\"go-toggle__text\">\n <span class=\"go-toggle__title\">Cho ph\u00E9p tr\u1EA1ng th\u00E1i \"1 ph\u1EA7n\"</span>\n <span class=\"go-toggle__sub\">T\u1EAFt \u0111\u1EC3 checkbox ch\u1EC9 c\u00F2n ch\u01B0a / xong</span>\n </span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n [checked]=\"allowPartial()\"\n (change)=\"setAllowPartial($any($event.target).checked)\"\n />\n </label>\n </div>\n\n <div class=\"go-list\">\n @for (item of items(); track item.id) {\n <div class=\"go-row\" (click)=\"openEdit(item)\">\n <div class=\"go-row__body\">\n <span class=\"go-row__title\">{{ item.title }}</span>\n @if (item.desc) {\n <span class=\"go-row__desc\">{{ item.desc }}</span>\n }\n </div>\n <button\n type=\"button\"\n class=\"go-icon-btn go-row__danger\"\n (click)=\"removeItem(item, $event)\"\n title=\"Xo\u00E1 m\u00F3n\"\n aria-label=\"Xo\u00E1 m\u00F3n\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n } @empty {\n <p class=\"go-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o. B\u1EA5m \u201CTh\u00EAm m\u00F3n\u201D \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u.</p>\n }\n </div>\n\n <div class=\"go-add-wrap\">\n <button type=\"button\" class=\"go-add\" (click)=\"openAdd()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n</section>\n\n@if (isDialogOpen()) {\n <app-going-out-edit-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [showStatus]=\"false\"\n [showNote]=\"false\"\n [canEditTitleDesc]=\"true\"\n [initialTitle]=\"draftTitle()\"\n [initialDesc]=\"draftDesc()\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSave($event)\"\n />\n}\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;--danger: #ef4444;--danger-soft: #fee2e2;position:fixed;inset:0;z-index:210;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:go-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes go-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.go-root{display:flex;flex-direction:column;flex:1;min-height:0}.go-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.go-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-back:hover{background:var(--primary-soft);color:var(--primary)}.go-header__title{margin:0;font-size:16px;font-weight:700}.go-header__spacer{flex:1}.go-perms{display:flex;flex-direction:column;gap:8px;padding:12px}.go-toggle{display:flex;align-items:center;gap:12px;padding:12px 14px;background:var(--surface);border:1px solid var(--border);border-radius:14px}.go-toggle__text{flex:1;display:flex;flex-direction:column;gap:2px}.go-toggle__title{font-size:14px;font-weight:700;color:var(--text)}.go-toggle__sub{font-size:12px;color:var(--muted)}.go-toggle input[type=checkbox]{width:40px;height:24px;flex-shrink:0;accent-color:var(--primary)}.go-list{flex:1;min-height:0;overflow:auto;-webkit-overflow-scrolling:touch;padding:0 12px 12px;display:flex;flex-direction:column;gap:8px}.go-row{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--surface);border:1px solid var(--border);border-radius:14px;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-row:active{background:var(--primary-soft)}.go-row__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.go-row__title{font-size:15px;font-weight:700;color:var(--text);word-break:break-word}.go-row__desc{font-size:13px;color:var(--muted);word-break:break-word}.go-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.go-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.go-row__danger{color:var(--danger)}.go-row__danger:hover{background:var(--danger-soft);color:var(--danger)}.go-empty{margin:24px 12px;text-align:center;font-size:14px;color:var(--muted)}.go-add-wrap{flex-shrink:0;padding:8px 12px calc(16px + env(safe-area-inset-bottom,0px))}.go-add{width:100%;border:1px dashed var(--primary);background:var(--primary-soft);color:var(--primary);border-radius:14px;padding:12px;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;-webkit-tap-highlight-color:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: GoingOutEditDialogComponent, selector: "app-going-out-edit-dialog", inputs: ["mode", "heading", "showStatus", "showNote", "showNotNeeded", "canEditTitleDesc", "allowPartial", "initialTitle", "initialDesc", "initialNote", "initialStatus", "initialNotNeeded", "showRemoveFromTrip"], outputs: ["cancel", "save", "removeFromTrip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
21776
22118
  }
21777
22119
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: GoingOutListComponent, decorators: [{
21778
22120
  type: Component,
@@ -21871,7 +22213,6 @@ class PreparingPurchaseService {
21871
22213
  this.cache = null;
21872
22214
  this.loadInFlight = null;
21873
22215
  this.bootstrap.invalidatePreparing();
21874
- this.bootstrap.invalidate('goingOutItems');
21875
22216
  }
21876
22217
  getProducts() {
21877
22218
  return this.loadLists().pipe(map((data) => [...data.products].sort((a, b) => a.name.localeCompare(b.name, 'vi'))));
@@ -21998,26 +22339,18 @@ class PreparingPurchaseService {
21998
22339
  }
21999
22340
  fetchListsFromSheet() {
22000
22341
  return this.bootstrap.loadPreparing().pipe(map(() => {
22001
- const productRows = this.bootstrap.getRows('preparingProducts') ??
22002
- this.bootstrap.getRows('goingOutItems') ??
22003
- [];
22004
- const legacyProductRows = this.bootstrap.getRows('preparingProductsLegacy') ?? [];
22342
+ const productRows = this.bootstrap.getRows('preparingProducts') ?? [];
22005
22343
  const requestRows = this.bootstrap.getRows('preparingRequests') ?? [];
22006
22344
  const commentRows = this.bootstrap.getRows('preparingComments') ?? [];
22007
- return this.buildLists(this.mergeProducts(this.parseProductRows(productRows), this.parseLegacyPreparingProductRows(legacyProductRows)), this.parseRequestRows(requestRows), this.parseCommentRows(commentRows));
22345
+ return this.buildLists(this.parsePreparingProductRows(productRows), this.parseRequestRows(requestRows), this.parseCommentRows(commentRows));
22008
22346
  }), catchError(() => forkJoin({
22009
22347
  products: this.readProducts().pipe(catchError(() => of([]))),
22010
22348
  requests: this.readRequests().pipe(catchError(() => of([]))),
22011
22349
  comments: this.readComments().pipe(catchError(() => of([]))),
22012
22350
  }).pipe(map(({ products, requests, comments }) => this.buildLists(products, requests, comments)))));
22013
22351
  }
22014
- parseProductRows(rows) {
22015
- return rows
22016
- .map((row, idx) => this.parseCatalogProductRow(row, idx + 2))
22017
- .filter((p) => !!p);
22018
- }
22019
- /** Danh mục cũ trên `Preparing!A:C`: ID | name | Description (hoặc name | Description). */
22020
- parseLegacyPreparingProductRows(rows) {
22352
+ /** Danh mục trên `Preparing!A:C`: ID | name | Description. */
22353
+ parsePreparingProductRows(rows) {
22021
22354
  return rows
22022
22355
  .map((row, idx) => {
22023
22356
  const colA = (row[0] || '').trim();
@@ -22042,46 +22375,6 @@ class PreparingPurchaseService {
22042
22375
  })
22043
22376
  .filter((p) => !!p);
22044
22377
  }
22045
- parseCatalogProductRow(row, rowIndex) {
22046
- const id = (row[0] || '').trim();
22047
- const deletedRaw = (row[5] || '').trim();
22048
- if (!id || this.isTruthy(deletedRaw))
22049
- return null;
22050
- let name = (row[1] || '').trim();
22051
- let description = (row[2] ?? '').toString().trim();
22052
- const createdBy = (row[3] || '').trim();
22053
- const createdAt = (row[4] || '').trim();
22054
- // Sau khi thêm cột ID: tên có thể còn nằm ở cột Description (J).
22055
- if (!name && description) {
22056
- name = description;
22057
- description = '';
22058
- }
22059
- // Title trùng UUID nhưng Description vẫn là tên hiển thị.
22060
- if (name && name === id && this.looksLikeTechnicalId(id) && description) {
22061
- name = description;
22062
- description = '';
22063
- }
22064
- if (!name)
22065
- return null;
22066
- return {
22067
- id,
22068
- name,
22069
- description,
22070
- createdBy: createdBy.toLowerCase(),
22071
- createdAt,
22072
- rowIndex,
22073
- };
22074
- }
22075
- mergeProducts(primary, legacy) {
22076
- const map = new Map();
22077
- for (const product of legacy) {
22078
- map.set(product.id, product);
22079
- }
22080
- for (const product of primary) {
22081
- map.set(product.id, product);
22082
- }
22083
- return [...map.values()];
22084
- }
22085
22378
  parseRequestRows(rows) {
22086
22379
  return rows
22087
22380
  .map((row, idx) => this.mapRequestRow(row, idx + 2))
@@ -22233,22 +22526,13 @@ class PreparingPurchaseService {
22233
22526
  this.cache.purchased = [purchased, ...this.cache.purchased].sort(this.sortByPurchasedDesc);
22234
22527
  }
22235
22528
  readProducts() {
22236
- return forkJoin({
22237
- catalog: this.googleSheets
22238
- .getSheetValues({
22239
- spreadsheetId: this.SHEET_ID,
22240
- sheetName: 'Settings',
22241
- range: 'H2:M',
22242
- })
22243
- .pipe(catchError(() => of([]))),
22244
- legacy: this.googleSheets
22245
- .getSheetValues({
22246
- spreadsheetId: this.SHEET_ID,
22247
- sheetName: this.SHEET_NAME,
22248
- range: 'A2:C',
22249
- })
22250
- .pipe(catchError(() => of([]))),
22251
- }).pipe(map(({ catalog, legacy }) => this.mergeProducts(this.parseProductRows(catalog), this.parseLegacyPreparingProductRows(legacy))));
22529
+ return this.googleSheets
22530
+ .getSheetValues({
22531
+ spreadsheetId: this.SHEET_ID,
22532
+ sheetName: this.SHEET_NAME,
22533
+ range: 'A2:C',
22534
+ })
22535
+ .pipe(catchError(() => of([])), map((rows) => this.parsePreparingProductRows(rows)));
22252
22536
  }
22253
22537
  readRequests() {
22254
22538
  return this.googleSheets
@@ -23068,16 +23352,22 @@ class PreparingPurchaseComponent {
23068
23352
  const month = String(now.getMonth() + 1).padStart(2, '0');
23069
23353
  return `${now.getFullYear()}-${month}`;
23070
23354
  }
23071
- load() {
23072
- const cached = this.svc.getCachedLists();
23073
- if (cached) {
23074
- this.applyLists(cached);
23075
- return;
23355
+ /** @param force When true (refresh), bypass cache and refetch from Sheet. */
23356
+ load(force = false) {
23357
+ if (!force) {
23358
+ const cached = this.svc.getCachedLists();
23359
+ if (cached) {
23360
+ this.applyLists(cached);
23361
+ return;
23362
+ }
23363
+ }
23364
+ else {
23365
+ this.svc.invalidateCache();
23076
23366
  }
23077
23367
  this.loading.set(true);
23078
23368
  this.errorMsg.set('');
23079
23369
  this.svc
23080
- .loadLists()
23370
+ .loadLists(force)
23081
23371
  .pipe(takeUntilDestroyed(this.destroyRef), catchError$1((err) => {
23082
23372
  this.errorMsg.set(err?.message || 'Không tải được danh sách');
23083
23373
  return of({
@@ -23449,7 +23739,7 @@ class PreparingPurchaseComponent {
23449
23739
  this.successMsg.set('');
23450
23740
  }
23451
23741
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: PreparingPurchaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
23452
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: PreparingPurchaseComponent, isStandalone: true, selector: "app-preparing-purchase", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back", pendingCountChange: "pendingCountChange" }, host: { listeners: { "document:click": "onDocumentClick()" } }, ngImport: i0, template: "<section class=\"pp-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"C\u1EA7n mua s\u1EAFm\">\n <header class=\"pp-header\">\n <button type=\"button\" class=\"pp-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"pp-header__title\">C\u1EA7n mua s\u1EAFm</h1>\n <span class=\"pp-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"pp-icon-btn\"\n (click)=\"load()\"\n [disabled]=\"loading()\"\n title=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n aria-label=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n >\n <i class=\"pi pi-refresh\" [class.pp-icon-btn__spin]=\"loading()\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n @if (errorMsg()) {\n <div class=\"pp-banner pp-banner--error\" role=\"alert\">\n <span>{{ errorMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n @if (successMsg()) {\n <div class=\"pp-banner pp-banner--success\" role=\"status\">\n <span>{{ successMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n <div class=\"pp-overview\" aria-label=\"T\u1ED5ng quan\">\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--amber\">{{ pendingItems().length }}</span>\n <span class=\"pp-overview__label\">Ch\u01B0a mua</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ highPriorityCount() }}</span>\n <span class=\"pp-overview__label\">\u01AFu ti\u00EAn cao</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ overdueCount() }}</span>\n <span class=\"pp-overview__label\">Qu\u00E1 h\u1EA1n</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--success\">{{ purchasedThisMonthCount() }}</span>\n <span class=\"pp-overview__label\">\u0110\u00E3 mua th\u00E1ng</span>\n </div>\n </div>\n\n @if (pendingItems().length > 0) {\n <div class=\"pp-priority-bar\" aria-hidden=\"true\">\n <div class=\"pp-priority-bar__track\">\n @for (segment of prioritySegments(); track segment.priority) {\n <span\n class=\"pp-priority-bar__segment\"\n [class.pp-priority-bar__segment--high]=\"segment.priority === 'high'\"\n [class.pp-priority-bar__segment--medium]=\"segment.priority === 'medium'\"\n [class.pp-priority-bar__segment--low]=\"segment.priority === 'low'\"\n [style.width.%]=\"segment.percent\"\n ></span>\n }\n </div>\n <div class=\"pp-priority-bar__legend\">\n <span>\u2191 Cao {{ priorityCounts().high }}</span>\n <span>\u2014 TB {{ priorityCounts().medium }}</span>\n <span>\u2193 Th\u1EA5p {{ priorityCounts().low }}</span>\n </div>\n </div>\n }\n\n <div class=\"pp-tabs\" role=\"tablist\" aria-label=\"Tr\u1EA1ng th\u00E1i mua\">\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'pending'\"\n [attr.aria-selected]=\"activeTab() === 'pending'\"\n (click)=\"setTab('pending')\"\n >\n C\u1EA7n mua <span class=\"pp-tabs__count\">{{ pendingItems().length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'purchased'\"\n [attr.aria-selected]=\"activeTab() === 'purchased'\"\n (click)=\"setTab('purchased')\"\n >\n \u0110\u00E3 mua <span class=\"pp-tabs__count\">{{ purchasedItems().length }}</span>\n </button>\n </div>\n\n @if (activeTab() === 'pending') {\n <div class=\"pp-filters\" role=\"group\" aria-label=\"L\u1ECDc theo \u01B0u ti\u00EAn\">\n @for (chip of priorityFilterOptions; track chip.id) {\n <button\n type=\"button\"\n class=\"pp-filter-chip\"\n [class.pp-filter-chip--active]=\"priorityFilter() === chip.id\"\n (click)=\"setPriorityFilter(chip.id)\"\n >\n {{ chip.label }}\n </button>\n }\n </div>\n }\n\n <div class=\"pp-list\">\n @if (activeTab() === 'pending') {\n @if (priorityFilter() === 'all') {\n @for (group of pendingPriorityGroups(); track group.priority) {\n @if (group.items.length > 0) {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"group.priority === 'high'\"\n [class.pp-group-label--medium]=\"group.priority === 'medium'\"\n [class.pp-group-label--low]=\"group.priority === 'low'\"\n >\n {{ priorityGroupHeading(group.priority) }}\n </p>\n @for (item of group.items; track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n }\n } @else {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"priorityFilter() === 'high'\"\n [class.pp-group-label--medium]=\"priorityFilter() === 'medium'\"\n [class.pp-group-label--low]=\"priorityFilter() === 'low'\"\n >\n {{ filteredPriorityHeading() }}\n </p>\n @for (item of filteredPendingItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n\n @if (filteredPendingItems().length === 0 && !loading()) {\n <p class=\"pp-empty\">Kh\u00F4ng c\u00F2n m\u00F3n n\u00E0o c\u1EA7n mua.</p>\n }\n } @else {\n @for (item of purchasedItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"purchasedRow; context: { $implicit: item }\" />\n } @empty {\n @if (!loading()) {\n <p class=\"pp-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o \u0111\u00E3 mua.</p>\n }\n }\n }\n\n @if (loading() && pendingItems().length === 0 && purchasedItems().length === 0) {\n <p class=\"pp-empty\">\u0110ang t\u1EA3i\u2026</p>\n }\n </div>\n\n <div class=\"pp-add-wrap\">\n <button type=\"button\" class=\"pp-add\" (click)=\"openAdd()\" [disabled]=\"saving()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n</section>\n\n<ng-template #pendingRow let-item>\n <article\n class=\"pp-row pp-row--pending pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n [class.pp-row--high]=\"item.priority === 'high'\"\n [class.pp-row--medium]=\"item.priority === 'medium'\"\n [class.pp-row--low]=\"item.priority === 'low'\"\n (click)=\"openPendingDetailItem(item)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPendingDetailItem(item)\"\n (keydown.space)=\"$event.preventDefault(); openPendingDetailItem(item)\"\n [attr.aria-label]=\"'Xem chi ti\u1EBFt ' + item.itemName\"\n >\n <span\n class=\"pp-priority\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n @if (rowSubtitle(item); as subtitle) {\n <p class=\"pp-row__desc\">{{ subtitle }}</p>\n }\n <p class=\"pp-row__meta\">\n Th\u00EAm {{ formatDate(item.createdDate) }}\n @if (item.createdBy) {\n \u00B7 {{ item.createdBy }}\n }\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__actions-col\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row__confirm\"\n (click)=\"openPurchaseConfirm(item)\"\n [disabled]=\"saving()\"\n title=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n aria-label=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"pp-row__menu-wrap\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPendingDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openEdit(item); rowMenuKey.set(null)\"\n >\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item pp-row-menu__item--danger\"\n role=\"menuitem\"\n (click)=\"askDelete(item, $event)\"\n >\n Xo\u00E1\n </button>\n </div>\n }\n </div>\n </div>\n </article>\n</ng-template>\n\n<ng-template #purchasedRow let-item>\n <article\n class=\"pp-row pp-row--purchased pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n (click)=\"openPurchaseDetail(item, $event)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPurchaseDetail(item, $event)\"\n (keydown.space)=\"$event.preventDefault(); openPurchaseDetailItem(item)\"\n [attr.aria-label]=\"'Xem l\u1ECBch s\u1EED mua ' + item.itemName\"\n >\n <span\n class=\"pp-priority pp-priority--muted\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n <p class=\"pp-row__desc\">\n @if (item.description) {\n {{ item.description }}\n } @else if (item.purchasedDate) {\n Mua {{ formatDate(item.purchasedDate) }}\n }\n </p>\n <p class=\"pp-row__meta\">\n @if (item.purchasedDate) {\n \u0110\u00E3 mua {{ formatDate(item.purchasedDate) }}\n }\n \u00B7 {{ purchaseCountForProduct(item.productId) }} l\u1EA7n\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__menu-wrap\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPurchaseDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n <span class=\"pp-row-menu__badge\">{{ purchaseCountForProduct(item.productId) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"duplicateFromPurchased(item, $event)\"\n >\n Mua l\u1EA1i\n </button>\n </div>\n }\n </div>\n </article>\n</ng-template>\n\n@if (isDialogOpen()) {\n <app-preparing-purchase-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [products]=\"products()\"\n [initialProductId]=\"dialogProductId()\"\n [initialProductName]=\"dialogProductName()\"\n [initialProductDescription]=\"dialogProductDescription()\"\n [initialAmount]=\"dialogAmount()\"\n [initialNote]=\"dialogNote()\"\n [initialAssignee]=\"dialogAssignee()\"\n [initialDueDate]=\"dialogDueDate()\"\n [initialPriority]=\"dialogPriority()\"\n [saving]=\"saving()\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSave($event)\"\n />\n}\n\n@if (purchaseConfirm()) {\n <app-preparing-purchase-confirm-dialog\n [itemName]=\"purchaseConfirm()?.itemName ?? ''\"\n [quantity]=\"purchaseConfirm()?.quantity ?? ''\"\n [saving]=\"saving()\"\n (cancel)=\"cancelPurchaseConfirm()\"\n (confirm)=\"onConfirmPurchase($event)\"\n />\n}\n\n@if (pendingDetailItem(); as detail) {\n <div class=\"pp-overlay pp-overlay--detail\" (click)=\"closePendingDetail()\" role=\"presentation\">\n <div\n class=\"pp-detail\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"detail.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-detail__hero\">\n <div class=\"pp-detail__hero-top\">\n <span\n class=\"pp-detail__priority\"\n [class.pp-detail__priority--high]=\"detail.priority === 'high'\"\n [class.pp-detail__priority--medium]=\"detail.priority === 'medium'\"\n [class.pp-detail__priority--low]=\"detail.priority === 'low'\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(detail.priority)\" aria-hidden=\"true\"></i>\n {{ priorityLabel(detail.priority) }}\n </span>\n <button type=\"button\" class=\"pp-icon-btn pp-icon-btn--soft\" (click)=\"closePendingDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <h2 class=\"pp-detail__name\">{{ detail.itemName }}</h2>\n <p class=\"pp-detail__meta\">\n <span>\n <i class=\"fa-solid fa-user\" aria-hidden=\"true\"></i>\n {{ detail.createdBy || 'guest' }}\n </span>\n <span>\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n {{ formatDateTime(detail.createdDate) }}\n </span>\n </p>\n </header>\n\n <div class=\"pp-detail__body\">\n <section class=\"pp-detail-card\" aria-label=\"Y\u00EAu c\u1EA7u mua\">\n <h3 class=\"pp-detail-card__title\">Y\u00EAu c\u1EA7u mua</h3>\n <dl class=\"pp-detail-kv\">\n <div class=\"pp-detail-kv__row\">\n <dt>S\u1ED1 l\u01B0\u1EE3ng</dt>\n <dd>{{ detail.quantity || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Ng\u01B0\u1EDDi ph\u1EE5 tr\u00E1ch</dt>\n <dd>{{ itemAssignee(detail) || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Th\u1EDDi h\u1EA1n mua</dt>\n <dd [class.pp-detail-kv__value--overdue]=\"isItemOverdue(detail)\">\n {{ itemDueDate(detail) || '\u2014' }}\n </dd>\n </div>\n </dl>\n </section>\n\n @if (detail.description?.trim()) {\n <section class=\"pp-detail-card\" aria-label=\"M\u00F4 t\u1EA3 s\u1EA3n ph\u1EA9m\">\n <h3 class=\"pp-detail-card__title\">M\u00F4 t\u1EA3</h3>\n <p class=\"pp-detail-card__text\">{{ detail.description }}</p>\n </section>\n }\n\n @if (itemNoteText(detail); as noteText) {\n <section class=\"pp-detail-card\" aria-label=\"Ghi ch\u00FA\">\n <h3 class=\"pp-detail-card__title\">Ghi ch\u00FA</h3>\n <p class=\"pp-detail-card__text\">{{ noteText }}</p>\n </section>\n }\n\n <section class=\"pp-comments\" aria-label=\"B\u00ECnh lu\u1EADn\">\n <div class=\"pp-comments__head\">\n <h3 class=\"pp-comments__title\">B\u00ECnh lu\u1EADn</h3>\n <span class=\"pp-comments__count\">{{ detailComments().length }}</span>\n </div>\n\n <ul class=\"pp-comments__list\">\n @for (comment of detailComments(); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDateTime(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">\n @for (part of commentSegments(comment.text); track $index) {\n @if (part.type === 'link') {\n <a\n class=\"pp-comment__link\"\n [href]=\"part.href\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >{{ part.value }}</a>\n } @else {\n {{ part.value }}\n }\n }\n </p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn \u2014 th\u00EAm ghi ch\u00FA cho m\u1ECDi ng\u01B0\u1EDDi c\u00F9ng xem.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"commentDraft()\"\n (input)=\"onCommentDraft($any($event.target).value)\"\n [disabled]=\"commentSaving()\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn\u2026\"\n aria-label=\"N\u1ED9i dung b\u00ECnh lu\u1EADn\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitComment()\"\n [disabled]=\"commentSaving() || !commentDraft().trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (commentSaving()) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-paper-plane\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </div>\n\n <footer class=\"pp-detail__foot\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--success pp-btn--block\"\n (click)=\"openPurchaseConfirm(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 mua\n </button>\n <div class=\"pp-detail__foot-row\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--ghost\"\n (click)=\"openEdit(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--danger-soft\"\n (click)=\"askDeleteFromDetail(detail)\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n X\u00F3a\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePendingDetail()\">\n \u0110\u00F3ng\n </button>\n </div>\n </footer>\n </div>\n </div>\n}\n\n@if (purchaseDetailProductId() && purchaseDetailItem(); as detailItem) {\n <div class=\"pp-overlay\" (click)=\"closePurchaseDetail()\" role=\"presentation\">\n <div\n class=\"pp-history-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'L\u1ECBch s\u1EED mua ' + detailItem.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-history-sheet__head\">\n <div>\n <h2 class=\"pp-history-sheet__title\">{{ detailItem.itemName }}</h2>\n <p class=\"pp-history-sheet__sub\">\n \u0110\u00E3 mua {{ purchaseHistoryRows().length }} l\u1EA7n\n </p>\n </div>\n <button type=\"button\" class=\"pp-icon-btn\" (click)=\"closePurchaseDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"pp-history-sheet__body\">\n @for (row of purchaseHistoryRows(); track itemKey(row); let idx = $index) {\n <article class=\"pp-purchase-entry\">\n <header class=\"pp-purchase-entry__head\">\n <span class=\"pp-purchase-entry__index\">L\u1EA7n {{ purchaseHistoryRows().length - idx }}</span>\n <span class=\"pp-purchase-entry__date\">\n {{ formatDate(row.purchasedDate ?? row.createdDate) }}\n </span>\n @if (row.quantity) {\n <span class=\"pp-purchase-entry__qty\">{{ row.quantity }}</span>\n }\n </header>\n\n @if (displayPurchaseNote(row) !== '\u2014') {\n <p class=\"pp-purchase-entry__note\">{{ displayPurchaseNote(row) }}</p>\n }\n\n <section class=\"pp-comments pp-comments--nested\" aria-label=\"B\u00ECnh lu\u1EADn l\u1EA7n mua\">\n <h3 class=\"pp-comments__title\">\n B\u00ECnh lu\u1EADn\n <span class=\"pp-comments__count\">{{ commentsForRequest(row.requestId).length }}</span>\n </h3>\n\n <ul class=\"pp-comments__list\">\n @for (comment of commentsForRequest(row.requestId); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDate(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">{{ comment.text }}</p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"historyCommentDraft(row.requestId)\"\n (input)=\"onHistoryCommentDraft(row.requestId, $any($event.target).value)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId)\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y\u2026\"\n [attr.aria-label]=\"'B\u00ECnh lu\u1EADn l\u1EA7n ' + (purchaseHistoryRows().length - idx)\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitHistoryComment(row)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId) || !historyCommentDraft(row.requestId).trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (isHistoryCommentSaving(row.requestId)) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"pi pi-send\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </article>\n }\n </div>\n\n <footer class=\"pp-history-sheet__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePurchaseDetail()\">\u0110\u00F3ng</button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--primary\"\n (click)=\"duplicateFromPurchased(detailItem, $event); closePurchaseDetail()\"\n >\n Mua l\u1EA1i\n </button>\n </footer>\n </div>\n </div>\n}\n\n@if (deleteConfirm()) {\n <div class=\"pp-overlay\" (click)=\"cancelDelete()\" role=\"presentation\">\n <div\n class=\"pp-confirm\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Xo\u00E1 m\u00F3n\"\n (click)=\"$event.stopPropagation()\"\n >\n <h2 class=\"pp-confirm__title\">Xo\u00E1 m\u00F3n n\u00E0y?</h2>\n <p class=\"pp-confirm__body\">{{ deleteConfirm()?.itemName }}</p>\n <div class=\"pp-confirm__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"cancelDelete()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--danger\" (click)=\"confirmDelete()\" [disabled]=\"saving()\">\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n \u0110ang xo\u00E1\u2026\n } @else {\n Xo\u00E1\n }\n </button>\n </div>\n </div>\n </div>\n}\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;--success-soft: #ecfdf5;--success: #059669;--warning-soft: #fff7ed;--warning: #ea580c;--danger: #e11d48;--danger-soft: #fff1f2;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:pp-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes pp-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.pp-root{display:flex;flex-direction:column;flex:1;min-height:0}.pp-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.pp-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-back:hover{background:var(--primary-soft);color:var(--primary)}.pp-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15}.pp-header__spacer{flex:1}.pp-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.pp-icon-btn--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-icon-btn--success{color:var(--success)}.pp-icon-btn--success:hover{background:var(--success-soft);color:var(--success)}.pp-icon-btn__spin{animation:pp-spin .8s linear infinite}@keyframes pp-spin{to{transform:rotate(360deg)}}.pp-banner{margin:10px 12px 0;padding:10px 12px;border-radius:12px;display:flex;align-items:center;gap:8px;font-size:13px}.pp-banner--error{background:var(--danger-soft);color:var(--danger)}.pp-banner--success{background:var(--success-soft);color:var(--success)}.pp-banner__close{margin-left:auto;border:none;background:transparent;color:inherit;cursor:pointer}.pp-overview{flex-shrink:0;display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:10px 12px 0}.pp-overview__cell{background:var(--surface);border:1px solid var(--border);border-radius:0;padding:8px 4px;text-align:center}.pp-overview__value{display:block;font-size:16px;font-weight:700;line-height:1.1}.pp-overview__value--amber{color:var(--warning)}.pp-overview__value--danger{color:var(--danger)}.pp-overview__value--success{color:var(--success)}.pp-overview__label{display:block;margin-top:2px;font-size:8px;color:var(--muted-2);line-height:1.2}.pp-priority-bar{flex-shrink:0;padding:8px 12px 0}.pp-priority-bar__track{display:flex;height:5px;overflow:hidden;background:var(--border)}.pp-priority-bar__segment{height:100%}.pp-priority-bar__segment--high{background:var(--danger)}.pp-priority-bar__segment--medium{background:#eab308}.pp-priority-bar__segment--low{background:#22c55e}.pp-priority-bar__legend{display:flex;gap:10px;margin-top:4px;font-size:9px;color:var(--muted)}.pp-tabs{flex-shrink:0;display:flex;margin:10px 12px 0;border:1px solid var(--border);background:#f7f8fc}.pp-tabs__btn{flex:1;border:none;background:transparent;padding:8px 0;font-size:11px;font-weight:700;color:var(--muted);cursor:pointer;font-family:inherit}.pp-tabs__btn:first-child{border-right:1px solid var(--border)}.pp-tabs__btn--active{background:var(--surface);color:var(--text)}.pp-tabs__count{color:var(--muted-2);font-weight:700}.pp-filters{flex-shrink:0;display:flex;gap:4px;padding:8px 12px 0;overflow-x:auto}.pp-filter-chip{flex-shrink:0;border:1px solid var(--border);border-radius:0;background:var(--surface);color:var(--muted);font-size:10px;font-weight:600;padding:4px 8px;cursor:pointer;font-family:inherit}.pp-filter-chip--active{border-color:var(--primary);background:var(--primary-soft);color:var(--primary)}.pp-group-label{margin:4px 0 0;font-size:10px;font-weight:700;letter-spacing:.03em}.pp-group-label--high{color:var(--danger)}.pp-group-label--medium{color:#d97706}.pp-group-label--low{color:var(--success)}.pp-list{flex:1;overflow:auto;padding:8px 12px 12px;display:flex;flex-direction:column;gap:5px}.pp-priority{flex-shrink:0;width:18px;padding-top:2px;display:inline-flex;align-items:flex-start;justify-content:center;font-size:12px;font-weight:700}.pp-priority--high{color:#e11d48}.pp-priority--medium{color:#eab308}.pp-priority--low{color:#22c55e}.pp-priority--muted{opacity:.55}.pp-row{position:relative;display:flex;gap:6px;align-items:flex-start;padding:9px 10px;background:var(--surface);border:1px solid var(--border);border-radius:0}.pp-row--pending{border-left-width:3px}.pp-row--high{border-left-color:var(--danger)}.pp-row--medium{border-left-color:#eab308}.pp-row--low{border-left-color:#22c55e}.pp-row--purchased{opacity:.95;background:#fafbfc}.pp-row--clickable{cursor:pointer;transition:border-color .15s ease,background .15s ease}.pp-row--clickable:hover{border-color:color-mix(in srgb,var(--primary) 35%,var(--border));background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.pp-row--menu-open{z-index:10;overflow:visible}.pp-row--clickable{cursor:pointer;transition:background .15s ease}.pp-row--clickable:hover{background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:-2px}.pp-row__body{flex:1;min-width:0}.pp-row__top{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:2px}.pp-row__main{flex:1;min-width:0}.pp-row__title{margin:0;font-size:14px;font-weight:700;line-height:1.25;min-width:0}.pp-row--compact .pp-row__title{font-size:14px}.pp-row__desc{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pp-row__qty{flex-shrink:0;font-size:12px;color:var(--primary);font-weight:700;white-space:nowrap}.pp-row__note{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}.pp-row__meta{margin:4px 0 0;font-size:9px;color:var(--muted-2);min-width:0}.pp-row__comments{display:inline-flex;align-items:center;gap:3px;white-space:nowrap}.pp-row__comments i{font-size:9px}.pp-row__actions-col{display:flex;flex-direction:column;align-items:center;gap:6px;flex-shrink:0}.pp-row__purchase-note{margin:0 0 6px;font-size:13px;color:var(--success);line-height:1.4}.pp-row__purchase-note i{margin-right:4px;font-size:12px}.pp-row__confirm{flex-shrink:0;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--success);border-radius:0;background:var(--success-soft);color:var(--success);font-size:12px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-row__confirm:hover{background:var(--success);color:#fff}.pp-row__confirm:disabled{opacity:.6;cursor:not-allowed}.pp-row__menu-wrap{position:relative;flex-shrink:0;align-self:center}.pp-icon-btn--menu{color:var(--muted-2)}.pp-row-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:148px;background:var(--surface);border:1px solid var(--border);border-radius:0;box-shadow:0 8px 24px #0f172a1f;padding:4px;z-index:20}.pp-row-menu__item{width:100%;border:none;background:transparent;border-radius:0;padding:8px 10px;display:inline-flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--text);cursor:pointer;text-align:left;font-family:inherit}.pp-row-menu__item:not(:last-child){border-bottom:1px solid var(--border)}.pp-row-menu__item:hover{background:var(--primary-soft);color:var(--primary)}.pp-row-menu__item--danger{color:var(--danger)}.pp-row-menu__item--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-row-menu__badge{margin-left:auto;font-size:11px;font-weight:700;padding:2px 7px;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-empty{margin:12px 0;text-align:center;color:var(--muted-2);font-size:14px}.pp-empty--group{margin:8px 0 0;font-size:13px}.pp-add-wrap{flex-shrink:0;padding:12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px));background:var(--surface);border-top:1px solid var(--border)}.pp-add{width:100%;border:none;border-radius:0;padding:11px 16px;background:var(--primary);color:#fff;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer}.pp-add:disabled{opacity:.6;cursor:not-allowed}.pp-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));padding-bottom:calc(16px + env(safe-area-inset-bottom,0px))}.pp-overlay--detail{background:#0f172a61;padding:12px}.pp-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:0;padding:18px 16px}.pp-confirm__title{margin:0 0 8px;font-size:16px}.pp-confirm__body{margin:0 0 16px;color:var(--muted);font-size:14px}.pp-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.pp-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer}.pp-btn--ghost{background:transparent;color:var(--muted)}.pp-btn--danger{background:var(--danger);color:#fff}.pp-btn--primary{background:var(--primary);color:#fff}.pp-btn--success{background:var(--success-soft);border:1px solid var(--success);color:var(--success)}.pp-btn--block{width:100%}.pp-btn--danger-soft{background:color-mix(in srgb,var(--danger) 10%,var(--surface));border:1px solid color-mix(in srgb,var(--danger) 35%,var(--border));color:var(--danger)}.pp-icon-btn--soft{width:34px;height:34px;border-radius:10px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail{width:100%;max-width:420px;max-height:min(90vh,720px);background:var(--surface);border:1px solid var(--border);border-radius:16px;display:flex;flex-direction:column;overflow:hidden}.pp-detail__hero{padding:16px 16px 12px;border-bottom:1px solid color-mix(in srgb,var(--border) 70%,transparent)}.pp-detail__hero-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}.pp-detail__priority{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail__priority--high{background:color-mix(in srgb,var(--danger) 12%,white);color:var(--danger)}.pp-detail__priority--medium{background:color-mix(in srgb,#d97706 12%,white);color:#d97706}.pp-detail__priority--low{background:color-mix(in srgb,var(--success) 12%,white);color:var(--success)}.pp-detail__priority .fa-solid{font-size:10px}.pp-detail__name{margin:0 0 8px;font-size:20px;font-weight:800;line-height:1.25;letter-spacing:-.02em;color:var(--text);overflow-wrap:anywhere}.pp-detail__meta{margin:0;display:flex;flex-wrap:wrap;gap:12px;font-size:12px;font-weight:600;color:var(--muted)}.pp-detail__meta span{display:inline-flex;align-items:center;gap:6px}.pp-detail__meta .fa-solid{font-size:11px;opacity:.75}.pp-detail__body{flex:1;overflow:auto;padding:14px 16px 16px;display:flex;flex-direction:column;gap:10px}.pp-detail__foot{display:flex;flex-direction:column;gap:8px;padding:12px 16px 16px;border-top:1px solid color-mix(in srgb,var(--border) 70%,transparent);background:color-mix(in srgb,var(--surface-2, #f8fafc) 50%,var(--surface))}.pp-detail__foot-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.pp-detail__foot-row .pp-btn{width:100%;padding:9px 8px;font-size:13px}.pp-detail-card{padding:12px 14px;border-radius:14px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent)}.pp-detail-card__title{margin:0 0 8px;font-size:12px;font-weight:700;color:var(--muted)}.pp-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:var(--text);overflow-wrap:anywhere}.pp-detail-kv{margin:0}.pp-detail-kv__row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:5px 0}.pp-detail-kv__row:not(:last-child){border-bottom:1px solid color-mix(in srgb,var(--border) 55%,transparent)}.pp-detail-kv__row dt{margin:0;font-size:12px;font-weight:500;color:var(--muted)}.pp-detail-kv__row dd{margin:0;font-size:13px;font-weight:700;color:var(--text);text-align:right;overflow-wrap:anywhere}.pp-detail-kv__value--overdue{color:var(--danger)}.pp-comments{margin-top:4px}.pp-comments__head{display:flex;align-items:center;gap:8px;margin-bottom:10px}.pp-comments__title{margin:0;font-size:13px;font-weight:700;color:var(--text)}.pp-comments__count{font-size:11px;font-weight:700;min-width:22px;height:22px;padding:0 7px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-comments__list{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:8px}.pp-comments__empty{font-size:12px;line-height:1.45;color:var(--muted-2);padding:10px 12px;border-radius:12px;background:var(--surface-2, #f8fafc);border:1px dashed color-mix(in srgb,var(--border) 90%,transparent)}.pp-comments__composer{display:flex;align-items:flex-end;gap:8px;margin-top:12px}.pp-comments__input{flex:1;min-width:0;resize:vertical;min-height:44px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--border) 90%,transparent);border-radius:12px;background:var(--surface);color:var(--text);font-size:13px;font-family:inherit;outline:none;line-height:1.4}.pp-comments__input:focus{border-color:var(--primary)}.pp-comments__send{flex-shrink:0;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:12px;background:var(--primary);color:#fff;font-size:14px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-comments__send:hover:not(:disabled){filter:brightness(1.05)}.pp-comments__send:disabled{opacity:.5;cursor:not-allowed}.pp-comment{border-radius:12px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent);padding:10px 12px}.pp-comment__head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:6px}.pp-comment__author{font-size:12px;font-weight:700;color:var(--text)}.pp-comment__date{font-size:10px;font-weight:600;color:var(--muted-2);font-variant-numeric:tabular-nums}.pp-comment__text{margin:0;font-size:13px;color:var(--muted);line-height:1.45;overflow-wrap:anywhere;white-space:pre-wrap}.pp-comment__link{color:var(--primary);text-decoration:underline;text-underline-offset:2px;word-break:break-all}.pp-comment__link:hover{filter:brightness(.92)}.pp-purchase-entry{border:1px solid var(--border);background:#fafbfc;padding:12px;margin-bottom:10px}.pp-purchase-entry:last-child{margin-bottom:0}.pp-purchase-entry__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;margin-bottom:8px}.pp-purchase-entry__index{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.03em;color:var(--primary)}.pp-purchase-entry__date{font-size:13px;font-weight:700;color:var(--text)}.pp-purchase-entry__qty{margin-left:auto;font-size:12px;font-weight:700;color:var(--muted)}.pp-purchase-entry__note{margin:0 0 10px;font-size:12px;line-height:1.4;color:var(--muted);overflow-wrap:anywhere}.pp-comments--nested{margin-top:0;padding-top:0;border-top:none}.pp-history-sheet{width:min(100%,520px);max-height:min(92vh,720px);margin:0 auto;background:var(--surface);border-radius:0;display:flex;flex-direction:column;overflow:hidden}.pp-history-sheet__head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:16px;border-bottom:1px solid var(--border)}.pp-history-sheet__title{margin:0;font-size:17px;font-weight:700}.pp-history-sheet__sub{margin:4px 0 0;font-size:13px;color:var(--muted)}.pp-history-sheet__body{flex:1;overflow:auto;padding:12px 16px}.pp-history-sheet__foot{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}.pp-history-table{width:100%;border-collapse:collapse;font-size:13px}.pp-history-table th,.pp-history-table td{padding:10px 8px;text-align:left;border-bottom:1px solid var(--border);vertical-align:top}.pp-history-table th{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--muted-2);background:#f8fafc;position:sticky;top:0}.pp-history-table tbody tr:last-child td{border-bottom:none}.pp-history-table td:first-child{width:36px;color:var(--muted-2);font-weight:700}.pp-history-table td:nth-child(3){white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: PreparingPurchaseDialogComponent, selector: "app-preparing-purchase-dialog", inputs: ["mode", "heading", "products", "initialProductId", "initialProductName", "initialProductDescription", "initialAmount", "initialNote", "initialAssignee", "initialDueDate", "initialPriority", "saving"], outputs: ["cancel", "save"] }, { kind: "component", type: PreparingPurchaseConfirmDialogComponent, selector: "app-preparing-purchase-confirm-dialog", inputs: ["itemName", "quantity", "saving"], outputs: ["cancel", "confirm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23742
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: PreparingPurchaseComponent, isStandalone: true, selector: "app-preparing-purchase", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back", pendingCountChange: "pendingCountChange" }, host: { listeners: { "document:click": "onDocumentClick()" } }, ngImport: i0, template: "<section class=\"pp-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"C\u1EA7n mua s\u1EAFm\">\n <header class=\"pp-header\">\n <button type=\"button\" class=\"pp-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"pp-header__title\">C\u1EA7n mua s\u1EAFm</h1>\n <span class=\"pp-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"pp-icon-btn\"\n (click)=\"load(true)\"\n [disabled]=\"loading()\"\n title=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n aria-label=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n >\n <i class=\"pi pi-refresh\" [class.pp-icon-btn__spin]=\"loading()\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n @if (errorMsg()) {\n <div class=\"pp-banner pp-banner--error\" role=\"alert\">\n <span>{{ errorMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n @if (successMsg()) {\n <div class=\"pp-banner pp-banner--success\" role=\"status\">\n <span>{{ successMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n <div class=\"pp-overview\" aria-label=\"T\u1ED5ng quan\">\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--amber\">{{ pendingItems().length }}</span>\n <span class=\"pp-overview__label\">Ch\u01B0a mua</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ highPriorityCount() }}</span>\n <span class=\"pp-overview__label\">\u01AFu ti\u00EAn cao</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ overdueCount() }}</span>\n <span class=\"pp-overview__label\">Qu\u00E1 h\u1EA1n</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--success\">{{ purchasedThisMonthCount() }}</span>\n <span class=\"pp-overview__label\">\u0110\u00E3 mua th\u00E1ng</span>\n </div>\n </div>\n\n @if (pendingItems().length > 0) {\n <div class=\"pp-priority-bar\" aria-hidden=\"true\">\n <div class=\"pp-priority-bar__track\">\n @for (segment of prioritySegments(); track segment.priority) {\n <span\n class=\"pp-priority-bar__segment\"\n [class.pp-priority-bar__segment--high]=\"segment.priority === 'high'\"\n [class.pp-priority-bar__segment--medium]=\"segment.priority === 'medium'\"\n [class.pp-priority-bar__segment--low]=\"segment.priority === 'low'\"\n [style.width.%]=\"segment.percent\"\n ></span>\n }\n </div>\n <div class=\"pp-priority-bar__legend\">\n <span>\u2191 Cao {{ priorityCounts().high }}</span>\n <span>\u2014 TB {{ priorityCounts().medium }}</span>\n <span>\u2193 Th\u1EA5p {{ priorityCounts().low }}</span>\n </div>\n </div>\n }\n\n <div class=\"pp-tabs\" role=\"tablist\" aria-label=\"Tr\u1EA1ng th\u00E1i mua\">\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'pending'\"\n [attr.aria-selected]=\"activeTab() === 'pending'\"\n (click)=\"setTab('pending')\"\n >\n C\u1EA7n mua <span class=\"pp-tabs__count\">{{ pendingItems().length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'purchased'\"\n [attr.aria-selected]=\"activeTab() === 'purchased'\"\n (click)=\"setTab('purchased')\"\n >\n \u0110\u00E3 mua <span class=\"pp-tabs__count\">{{ purchasedItems().length }}</span>\n </button>\n </div>\n\n @if (activeTab() === 'pending') {\n <div class=\"pp-filters\" role=\"group\" aria-label=\"L\u1ECDc theo \u01B0u ti\u00EAn\">\n @for (chip of priorityFilterOptions; track chip.id) {\n <button\n type=\"button\"\n class=\"pp-filter-chip\"\n [class.pp-filter-chip--active]=\"priorityFilter() === chip.id\"\n (click)=\"setPriorityFilter(chip.id)\"\n >\n {{ chip.label }}\n </button>\n }\n </div>\n }\n\n <div class=\"pp-list\">\n @if (activeTab() === 'pending') {\n @if (priorityFilter() === 'all') {\n @for (group of pendingPriorityGroups(); track group.priority) {\n @if (group.items.length > 0) {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"group.priority === 'high'\"\n [class.pp-group-label--medium]=\"group.priority === 'medium'\"\n [class.pp-group-label--low]=\"group.priority === 'low'\"\n >\n {{ priorityGroupHeading(group.priority) }}\n </p>\n @for (item of group.items; track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n }\n } @else {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"priorityFilter() === 'high'\"\n [class.pp-group-label--medium]=\"priorityFilter() === 'medium'\"\n [class.pp-group-label--low]=\"priorityFilter() === 'low'\"\n >\n {{ filteredPriorityHeading() }}\n </p>\n @for (item of filteredPendingItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n\n @if (filteredPendingItems().length === 0 && !loading()) {\n <p class=\"pp-empty\">Kh\u00F4ng c\u00F2n m\u00F3n n\u00E0o c\u1EA7n mua.</p>\n }\n } @else {\n @for (item of purchasedItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"purchasedRow; context: { $implicit: item }\" />\n } @empty {\n @if (!loading()) {\n <p class=\"pp-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o \u0111\u00E3 mua.</p>\n }\n }\n }\n\n @if (loading() && pendingItems().length === 0 && purchasedItems().length === 0) {\n <p class=\"pp-empty\">\u0110ang t\u1EA3i\u2026</p>\n }\n </div>\n\n <div class=\"pp-add-wrap\">\n <button type=\"button\" class=\"pp-add\" (click)=\"openAdd()\" [disabled]=\"saving()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n</section>\n\n<ng-template #pendingRow let-item>\n <article\n class=\"pp-row pp-row--pending pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n [class.pp-row--high]=\"item.priority === 'high'\"\n [class.pp-row--medium]=\"item.priority === 'medium'\"\n [class.pp-row--low]=\"item.priority === 'low'\"\n (click)=\"openPendingDetailItem(item)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPendingDetailItem(item)\"\n (keydown.space)=\"$event.preventDefault(); openPendingDetailItem(item)\"\n [attr.aria-label]=\"'Xem chi ti\u1EBFt ' + item.itemName\"\n >\n <span\n class=\"pp-priority\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n @if (rowSubtitle(item); as subtitle) {\n <p class=\"pp-row__desc\">{{ subtitle }}</p>\n }\n <p class=\"pp-row__meta\">\n Th\u00EAm {{ formatDate(item.createdDate) }}\n @if (item.createdBy) {\n \u00B7 {{ item.createdBy }}\n }\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__actions-col\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row__confirm\"\n (click)=\"openPurchaseConfirm(item)\"\n [disabled]=\"saving()\"\n title=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n aria-label=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"pp-row__menu-wrap\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPendingDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openEdit(item); rowMenuKey.set(null)\"\n >\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item pp-row-menu__item--danger\"\n role=\"menuitem\"\n (click)=\"askDelete(item, $event)\"\n >\n Xo\u00E1\n </button>\n </div>\n }\n </div>\n </div>\n </article>\n</ng-template>\n\n<ng-template #purchasedRow let-item>\n <article\n class=\"pp-row pp-row--purchased pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n (click)=\"openPurchaseDetail(item, $event)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPurchaseDetail(item, $event)\"\n (keydown.space)=\"$event.preventDefault(); openPurchaseDetailItem(item)\"\n [attr.aria-label]=\"'Xem l\u1ECBch s\u1EED mua ' + item.itemName\"\n >\n <span\n class=\"pp-priority pp-priority--muted\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n <p class=\"pp-row__desc\">\n @if (item.description) {\n {{ item.description }}\n } @else if (item.purchasedDate) {\n Mua {{ formatDate(item.purchasedDate) }}\n }\n </p>\n <p class=\"pp-row__meta\">\n @if (item.purchasedDate) {\n \u0110\u00E3 mua {{ formatDate(item.purchasedDate) }}\n }\n \u00B7 {{ purchaseCountForProduct(item.productId) }} l\u1EA7n\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__menu-wrap\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPurchaseDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n <span class=\"pp-row-menu__badge\">{{ purchaseCountForProduct(item.productId) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"duplicateFromPurchased(item, $event)\"\n >\n Mua l\u1EA1i\n </button>\n </div>\n }\n </div>\n </article>\n</ng-template>\n\n@if (isDialogOpen()) {\n <app-preparing-purchase-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [products]=\"products()\"\n [initialProductId]=\"dialogProductId()\"\n [initialProductName]=\"dialogProductName()\"\n [initialProductDescription]=\"dialogProductDescription()\"\n [initialAmount]=\"dialogAmount()\"\n [initialNote]=\"dialogNote()\"\n [initialAssignee]=\"dialogAssignee()\"\n [initialDueDate]=\"dialogDueDate()\"\n [initialPriority]=\"dialogPriority()\"\n [saving]=\"saving()\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSave($event)\"\n />\n}\n\n@if (purchaseConfirm()) {\n <app-preparing-purchase-confirm-dialog\n [itemName]=\"purchaseConfirm()?.itemName ?? ''\"\n [quantity]=\"purchaseConfirm()?.quantity ?? ''\"\n [saving]=\"saving()\"\n (cancel)=\"cancelPurchaseConfirm()\"\n (confirm)=\"onConfirmPurchase($event)\"\n />\n}\n\n@if (pendingDetailItem(); as detail) {\n <div class=\"pp-overlay pp-overlay--detail\" (click)=\"closePendingDetail()\" role=\"presentation\">\n <div\n class=\"pp-detail\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"detail.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-detail__hero\">\n <div class=\"pp-detail__hero-top\">\n <span\n class=\"pp-detail__priority\"\n [class.pp-detail__priority--high]=\"detail.priority === 'high'\"\n [class.pp-detail__priority--medium]=\"detail.priority === 'medium'\"\n [class.pp-detail__priority--low]=\"detail.priority === 'low'\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(detail.priority)\" aria-hidden=\"true\"></i>\n {{ priorityLabel(detail.priority) }}\n </span>\n <button type=\"button\" class=\"pp-icon-btn pp-icon-btn--soft\" (click)=\"closePendingDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <h2 class=\"pp-detail__name\">{{ detail.itemName }}</h2>\n <p class=\"pp-detail__meta\">\n <span>\n <i class=\"fa-solid fa-user\" aria-hidden=\"true\"></i>\n {{ detail.createdBy || 'guest' }}\n </span>\n <span>\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n {{ formatDateTime(detail.createdDate) }}\n </span>\n </p>\n </header>\n\n <div class=\"pp-detail__body\">\n <section class=\"pp-detail-card\" aria-label=\"Y\u00EAu c\u1EA7u mua\">\n <h3 class=\"pp-detail-card__title\">Y\u00EAu c\u1EA7u mua</h3>\n <dl class=\"pp-detail-kv\">\n <div class=\"pp-detail-kv__row\">\n <dt>S\u1ED1 l\u01B0\u1EE3ng</dt>\n <dd>{{ detail.quantity || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Ng\u01B0\u1EDDi ph\u1EE5 tr\u00E1ch</dt>\n <dd>{{ itemAssignee(detail) || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Th\u1EDDi h\u1EA1n mua</dt>\n <dd [class.pp-detail-kv__value--overdue]=\"isItemOverdue(detail)\">\n {{ itemDueDate(detail) || '\u2014' }}\n </dd>\n </div>\n </dl>\n </section>\n\n @if (detail.description?.trim()) {\n <section class=\"pp-detail-card\" aria-label=\"M\u00F4 t\u1EA3 s\u1EA3n ph\u1EA9m\">\n <h3 class=\"pp-detail-card__title\">M\u00F4 t\u1EA3</h3>\n <p class=\"pp-detail-card__text\">{{ detail.description }}</p>\n </section>\n }\n\n @if (itemNoteText(detail); as noteText) {\n <section class=\"pp-detail-card\" aria-label=\"Ghi ch\u00FA\">\n <h3 class=\"pp-detail-card__title\">Ghi ch\u00FA</h3>\n <p class=\"pp-detail-card__text\">{{ noteText }}</p>\n </section>\n }\n\n <section class=\"pp-comments\" aria-label=\"B\u00ECnh lu\u1EADn\">\n <div class=\"pp-comments__head\">\n <h3 class=\"pp-comments__title\">B\u00ECnh lu\u1EADn</h3>\n <span class=\"pp-comments__count\">{{ detailComments().length }}</span>\n </div>\n\n <ul class=\"pp-comments__list\">\n @for (comment of detailComments(); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDateTime(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">\n @for (part of commentSegments(comment.text); track $index) {\n @if (part.type === 'link') {\n <a\n class=\"pp-comment__link\"\n [href]=\"part.href\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >{{ part.value }}</a>\n } @else {\n {{ part.value }}\n }\n }\n </p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn \u2014 th\u00EAm ghi ch\u00FA cho m\u1ECDi ng\u01B0\u1EDDi c\u00F9ng xem.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"commentDraft()\"\n (input)=\"onCommentDraft($any($event.target).value)\"\n [disabled]=\"commentSaving()\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn\u2026\"\n aria-label=\"N\u1ED9i dung b\u00ECnh lu\u1EADn\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitComment()\"\n [disabled]=\"commentSaving() || !commentDraft().trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (commentSaving()) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-paper-plane\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </div>\n\n <footer class=\"pp-detail__foot\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--success pp-btn--block\"\n (click)=\"openPurchaseConfirm(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 mua\n </button>\n <div class=\"pp-detail__foot-row\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--ghost\"\n (click)=\"openEdit(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--danger-soft\"\n (click)=\"askDeleteFromDetail(detail)\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n X\u00F3a\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePendingDetail()\">\n \u0110\u00F3ng\n </button>\n </div>\n </footer>\n </div>\n </div>\n}\n\n@if (purchaseDetailProductId() && purchaseDetailItem(); as detailItem) {\n <div class=\"pp-overlay\" (click)=\"closePurchaseDetail()\" role=\"presentation\">\n <div\n class=\"pp-history-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'L\u1ECBch s\u1EED mua ' + detailItem.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-history-sheet__head\">\n <div>\n <h2 class=\"pp-history-sheet__title\">{{ detailItem.itemName }}</h2>\n <p class=\"pp-history-sheet__sub\">\n \u0110\u00E3 mua {{ purchaseHistoryRows().length }} l\u1EA7n\n </p>\n </div>\n <button type=\"button\" class=\"pp-icon-btn\" (click)=\"closePurchaseDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"pp-history-sheet__body\">\n @for (row of purchaseHistoryRows(); track itemKey(row); let idx = $index) {\n <article class=\"pp-purchase-entry\">\n <header class=\"pp-purchase-entry__head\">\n <span class=\"pp-purchase-entry__index\">L\u1EA7n {{ purchaseHistoryRows().length - idx }}</span>\n <span class=\"pp-purchase-entry__date\">\n {{ formatDate(row.purchasedDate ?? row.createdDate) }}\n </span>\n @if (row.quantity) {\n <span class=\"pp-purchase-entry__qty\">{{ row.quantity }}</span>\n }\n </header>\n\n @if (displayPurchaseNote(row) !== '\u2014') {\n <p class=\"pp-purchase-entry__note\">{{ displayPurchaseNote(row) }}</p>\n }\n\n <section class=\"pp-comments pp-comments--nested\" aria-label=\"B\u00ECnh lu\u1EADn l\u1EA7n mua\">\n <h3 class=\"pp-comments__title\">\n B\u00ECnh lu\u1EADn\n <span class=\"pp-comments__count\">{{ commentsForRequest(row.requestId).length }}</span>\n </h3>\n\n <ul class=\"pp-comments__list\">\n @for (comment of commentsForRequest(row.requestId); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDate(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">{{ comment.text }}</p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"historyCommentDraft(row.requestId)\"\n (input)=\"onHistoryCommentDraft(row.requestId, $any($event.target).value)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId)\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y\u2026\"\n [attr.aria-label]=\"'B\u00ECnh lu\u1EADn l\u1EA7n ' + (purchaseHistoryRows().length - idx)\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitHistoryComment(row)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId) || !historyCommentDraft(row.requestId).trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (isHistoryCommentSaving(row.requestId)) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"pi pi-send\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </article>\n }\n </div>\n\n <footer class=\"pp-history-sheet__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePurchaseDetail()\">\u0110\u00F3ng</button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--primary\"\n (click)=\"duplicateFromPurchased(detailItem, $event); closePurchaseDetail()\"\n >\n Mua l\u1EA1i\n </button>\n </footer>\n </div>\n </div>\n}\n\n@if (deleteConfirm()) {\n <div class=\"pp-overlay\" (click)=\"cancelDelete()\" role=\"presentation\">\n <div\n class=\"pp-confirm\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Xo\u00E1 m\u00F3n\"\n (click)=\"$event.stopPropagation()\"\n >\n <h2 class=\"pp-confirm__title\">Xo\u00E1 m\u00F3n n\u00E0y?</h2>\n <p class=\"pp-confirm__body\">{{ deleteConfirm()?.itemName }}</p>\n <div class=\"pp-confirm__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"cancelDelete()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--danger\" (click)=\"confirmDelete()\" [disabled]=\"saving()\">\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n \u0110ang xo\u00E1\u2026\n } @else {\n Xo\u00E1\n }\n </button>\n </div>\n </div>\n </div>\n}\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;--success-soft: #ecfdf5;--success: #059669;--warning-soft: #fff7ed;--warning: #ea580c;--danger: #e11d48;--danger-soft: #fff1f2;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:pp-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes pp-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.pp-root{display:flex;flex-direction:column;flex:1;min-height:0}.pp-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.pp-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-back:hover{background:var(--primary-soft);color:var(--primary)}.pp-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15}.pp-header__spacer{flex:1}.pp-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.pp-icon-btn--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-icon-btn--success{color:var(--success)}.pp-icon-btn--success:hover{background:var(--success-soft);color:var(--success)}.pp-icon-btn__spin{animation:pp-spin .8s linear infinite}@keyframes pp-spin{to{transform:rotate(360deg)}}.pp-banner{margin:10px 12px 0;padding:10px 12px;border-radius:12px;display:flex;align-items:center;gap:8px;font-size:13px}.pp-banner--error{background:var(--danger-soft);color:var(--danger)}.pp-banner--success{background:var(--success-soft);color:var(--success)}.pp-banner__close{margin-left:auto;border:none;background:transparent;color:inherit;cursor:pointer}.pp-overview{flex-shrink:0;display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:10px 12px 0}.pp-overview__cell{background:var(--surface);border:1px solid var(--border);border-radius:0;padding:8px 4px;text-align:center}.pp-overview__value{display:block;font-size:16px;font-weight:700;line-height:1.1}.pp-overview__value--amber{color:var(--warning)}.pp-overview__value--danger{color:var(--danger)}.pp-overview__value--success{color:var(--success)}.pp-overview__label{display:block;margin-top:2px;font-size:8px;color:var(--muted-2);line-height:1.2}.pp-priority-bar{flex-shrink:0;padding:8px 12px 0}.pp-priority-bar__track{display:flex;height:5px;overflow:hidden;background:var(--border)}.pp-priority-bar__segment{height:100%}.pp-priority-bar__segment--high{background:var(--danger)}.pp-priority-bar__segment--medium{background:#eab308}.pp-priority-bar__segment--low{background:#22c55e}.pp-priority-bar__legend{display:flex;gap:10px;margin-top:4px;font-size:9px;color:var(--muted)}.pp-tabs{flex-shrink:0;display:flex;margin:10px 12px 0;border:1px solid var(--border);background:#f7f8fc}.pp-tabs__btn{flex:1;border:none;background:transparent;padding:8px 0;font-size:11px;font-weight:700;color:var(--muted);cursor:pointer;font-family:inherit}.pp-tabs__btn:first-child{border-right:1px solid var(--border)}.pp-tabs__btn--active{background:var(--surface);color:var(--text)}.pp-tabs__count{color:var(--muted-2);font-weight:700}.pp-filters{flex-shrink:0;display:flex;gap:4px;padding:8px 12px 0;overflow-x:auto}.pp-filter-chip{flex-shrink:0;border:1px solid var(--border);border-radius:0;background:var(--surface);color:var(--muted);font-size:10px;font-weight:600;padding:4px 8px;cursor:pointer;font-family:inherit}.pp-filter-chip--active{border-color:var(--primary);background:var(--primary-soft);color:var(--primary)}.pp-group-label{margin:4px 0 0;font-size:10px;font-weight:700;letter-spacing:.03em}.pp-group-label--high{color:var(--danger)}.pp-group-label--medium{color:#d97706}.pp-group-label--low{color:var(--success)}.pp-list{flex:1;overflow:auto;padding:8px 12px 12px;display:flex;flex-direction:column;gap:5px}.pp-priority{flex-shrink:0;width:18px;padding-top:2px;display:inline-flex;align-items:flex-start;justify-content:center;font-size:12px;font-weight:700}.pp-priority--high{color:#e11d48}.pp-priority--medium{color:#eab308}.pp-priority--low{color:#22c55e}.pp-priority--muted{opacity:.55}.pp-row{position:relative;display:flex;gap:6px;align-items:flex-start;padding:9px 10px;background:var(--surface);border:1px solid var(--border);border-radius:0}.pp-row--pending{border-left-width:3px}.pp-row--high{border-left-color:var(--danger)}.pp-row--medium{border-left-color:#eab308}.pp-row--low{border-left-color:#22c55e}.pp-row--purchased{opacity:.95;background:#fafbfc}.pp-row--clickable{cursor:pointer;transition:border-color .15s ease,background .15s ease}.pp-row--clickable:hover{border-color:color-mix(in srgb,var(--primary) 35%,var(--border));background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.pp-row--menu-open{z-index:10;overflow:visible}.pp-row--clickable{cursor:pointer;transition:background .15s ease}.pp-row--clickable:hover{background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:-2px}.pp-row__body{flex:1;min-width:0}.pp-row__top{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:2px}.pp-row__main{flex:1;min-width:0}.pp-row__title{margin:0;font-size:14px;font-weight:700;line-height:1.25;min-width:0}.pp-row--compact .pp-row__title{font-size:14px}.pp-row__desc{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pp-row__qty{flex-shrink:0;font-size:12px;color:var(--primary);font-weight:700;white-space:nowrap}.pp-row__note{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}.pp-row__meta{margin:4px 0 0;font-size:9px;color:var(--muted-2);min-width:0}.pp-row__comments{display:inline-flex;align-items:center;gap:3px;white-space:nowrap}.pp-row__comments i{font-size:9px}.pp-row__actions-col{display:flex;flex-direction:column;align-items:center;gap:6px;flex-shrink:0}.pp-row__purchase-note{margin:0 0 6px;font-size:13px;color:var(--success);line-height:1.4}.pp-row__purchase-note i{margin-right:4px;font-size:12px}.pp-row__confirm{flex-shrink:0;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--success);border-radius:0;background:var(--success-soft);color:var(--success);font-size:12px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-row__confirm:hover{background:var(--success);color:#fff}.pp-row__confirm:disabled{opacity:.6;cursor:not-allowed}.pp-row__menu-wrap{position:relative;flex-shrink:0;align-self:center}.pp-icon-btn--menu{color:var(--muted-2)}.pp-row-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:148px;background:var(--surface);border:1px solid var(--border);border-radius:0;box-shadow:0 8px 24px #0f172a1f;padding:4px;z-index:20}.pp-row-menu__item{width:100%;border:none;background:transparent;border-radius:0;padding:8px 10px;display:inline-flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--text);cursor:pointer;text-align:left;font-family:inherit}.pp-row-menu__item:not(:last-child){border-bottom:1px solid var(--border)}.pp-row-menu__item:hover{background:var(--primary-soft);color:var(--primary)}.pp-row-menu__item--danger{color:var(--danger)}.pp-row-menu__item--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-row-menu__badge{margin-left:auto;font-size:11px;font-weight:700;padding:2px 7px;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-empty{margin:12px 0;text-align:center;color:var(--muted-2);font-size:14px}.pp-empty--group{margin:8px 0 0;font-size:13px}.pp-add-wrap{flex-shrink:0;padding:12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px));background:var(--surface);border-top:1px solid var(--border)}.pp-add{width:100%;border:none;border-radius:0;padding:11px 16px;background:var(--primary);color:#fff;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer}.pp-add:disabled{opacity:.6;cursor:not-allowed}.pp-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));padding-bottom:calc(16px + env(safe-area-inset-bottom,0px))}.pp-overlay--detail{background:#0f172a61;padding:12px}.pp-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:0;padding:18px 16px}.pp-confirm__title{margin:0 0 8px;font-size:16px}.pp-confirm__body{margin:0 0 16px;color:var(--muted);font-size:14px}.pp-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.pp-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer}.pp-btn--ghost{background:transparent;color:var(--muted)}.pp-btn--danger{background:var(--danger);color:#fff}.pp-btn--primary{background:var(--primary);color:#fff}.pp-btn--success{background:var(--success-soft);border:1px solid var(--success);color:var(--success)}.pp-btn--block{width:100%}.pp-btn--danger-soft{background:color-mix(in srgb,var(--danger) 10%,var(--surface));border:1px solid color-mix(in srgb,var(--danger) 35%,var(--border));color:var(--danger)}.pp-icon-btn--soft{width:34px;height:34px;border-radius:10px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail{width:100%;max-width:420px;max-height:min(90vh,720px);background:var(--surface);border:1px solid var(--border);border-radius:16px;display:flex;flex-direction:column;overflow:hidden}.pp-detail__hero{padding:16px 16px 12px;border-bottom:1px solid color-mix(in srgb,var(--border) 70%,transparent)}.pp-detail__hero-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}.pp-detail__priority{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail__priority--high{background:color-mix(in srgb,var(--danger) 12%,white);color:var(--danger)}.pp-detail__priority--medium{background:color-mix(in srgb,#d97706 12%,white);color:#d97706}.pp-detail__priority--low{background:color-mix(in srgb,var(--success) 12%,white);color:var(--success)}.pp-detail__priority .fa-solid{font-size:10px}.pp-detail__name{margin:0 0 8px;font-size:20px;font-weight:800;line-height:1.25;letter-spacing:-.02em;color:var(--text);overflow-wrap:anywhere}.pp-detail__meta{margin:0;display:flex;flex-wrap:wrap;gap:12px;font-size:12px;font-weight:600;color:var(--muted)}.pp-detail__meta span{display:inline-flex;align-items:center;gap:6px}.pp-detail__meta .fa-solid{font-size:11px;opacity:.75}.pp-detail__body{flex:1;overflow:auto;padding:14px 16px 16px;display:flex;flex-direction:column;gap:10px}.pp-detail__foot{display:flex;flex-direction:column;gap:8px;padding:12px 16px 16px;border-top:1px solid color-mix(in srgb,var(--border) 70%,transparent);background:color-mix(in srgb,var(--surface-2, #f8fafc) 50%,var(--surface))}.pp-detail__foot-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.pp-detail__foot-row .pp-btn{width:100%;padding:9px 8px;font-size:13px}.pp-detail-card{padding:12px 14px;border-radius:14px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent)}.pp-detail-card__title{margin:0 0 8px;font-size:12px;font-weight:700;color:var(--muted)}.pp-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:var(--text);overflow-wrap:anywhere}.pp-detail-kv{margin:0}.pp-detail-kv__row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:5px 0}.pp-detail-kv__row:not(:last-child){border-bottom:1px solid color-mix(in srgb,var(--border) 55%,transparent)}.pp-detail-kv__row dt{margin:0;font-size:12px;font-weight:500;color:var(--muted)}.pp-detail-kv__row dd{margin:0;font-size:13px;font-weight:700;color:var(--text);text-align:right;overflow-wrap:anywhere}.pp-detail-kv__value--overdue{color:var(--danger)}.pp-comments{margin-top:4px}.pp-comments__head{display:flex;align-items:center;gap:8px;margin-bottom:10px}.pp-comments__title{margin:0;font-size:13px;font-weight:700;color:var(--text)}.pp-comments__count{font-size:11px;font-weight:700;min-width:22px;height:22px;padding:0 7px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-comments__list{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:8px}.pp-comments__empty{font-size:12px;line-height:1.45;color:var(--muted-2);padding:10px 12px;border-radius:12px;background:var(--surface-2, #f8fafc);border:1px dashed color-mix(in srgb,var(--border) 90%,transparent)}.pp-comments__composer{display:flex;align-items:flex-end;gap:8px;margin-top:12px}.pp-comments__input{flex:1;min-width:0;resize:vertical;min-height:44px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--border) 90%,transparent);border-radius:12px;background:var(--surface);color:var(--text);font-size:13px;font-family:inherit;outline:none;line-height:1.4}.pp-comments__input:focus{border-color:var(--primary)}.pp-comments__send{flex-shrink:0;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:12px;background:var(--primary);color:#fff;font-size:14px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-comments__send:hover:not(:disabled){filter:brightness(1.05)}.pp-comments__send:disabled{opacity:.5;cursor:not-allowed}.pp-comment{border-radius:12px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent);padding:10px 12px}.pp-comment__head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:6px}.pp-comment__author{font-size:12px;font-weight:700;color:var(--text)}.pp-comment__date{font-size:10px;font-weight:600;color:var(--muted-2);font-variant-numeric:tabular-nums}.pp-comment__text{margin:0;font-size:13px;color:var(--muted);line-height:1.45;overflow-wrap:anywhere;white-space:pre-wrap}.pp-comment__link{color:var(--primary);text-decoration:underline;text-underline-offset:2px;word-break:break-all}.pp-comment__link:hover{filter:brightness(.92)}.pp-purchase-entry{border:1px solid var(--border);background:#fafbfc;padding:12px;margin-bottom:10px}.pp-purchase-entry:last-child{margin-bottom:0}.pp-purchase-entry__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;margin-bottom:8px}.pp-purchase-entry__index{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.03em;color:var(--primary)}.pp-purchase-entry__date{font-size:13px;font-weight:700;color:var(--text)}.pp-purchase-entry__qty{margin-left:auto;font-size:12px;font-weight:700;color:var(--muted)}.pp-purchase-entry__note{margin:0 0 10px;font-size:12px;line-height:1.4;color:var(--muted);overflow-wrap:anywhere}.pp-comments--nested{margin-top:0;padding-top:0;border-top:none}.pp-history-sheet{width:min(100%,520px);max-height:min(92vh,720px);margin:0 auto;background:var(--surface);border-radius:0;display:flex;flex-direction:column;overflow:hidden}.pp-history-sheet__head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:16px;border-bottom:1px solid var(--border)}.pp-history-sheet__title{margin:0;font-size:17px;font-weight:700}.pp-history-sheet__sub{margin:4px 0 0;font-size:13px;color:var(--muted)}.pp-history-sheet__body{flex:1;overflow:auto;padding:12px 16px}.pp-history-sheet__foot{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}.pp-history-table{width:100%;border-collapse:collapse;font-size:13px}.pp-history-table th,.pp-history-table td{padding:10px 8px;text-align:left;border-bottom:1px solid var(--border);vertical-align:top}.pp-history-table th{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--muted-2);background:#f8fafc;position:sticky;top:0}.pp-history-table tbody tr:last-child td{border-bottom:none}.pp-history-table td:first-child{width:36px;color:var(--muted-2);font-weight:700}.pp-history-table td:nth-child(3){white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: PreparingPurchaseDialogComponent, selector: "app-preparing-purchase-dialog", inputs: ["mode", "heading", "products", "initialProductId", "initialProductName", "initialProductDescription", "initialAmount", "initialNote", "initialAssignee", "initialDueDate", "initialPriority", "saving"], outputs: ["cancel", "save"] }, { kind: "component", type: PreparingPurchaseConfirmDialogComponent, selector: "app-preparing-purchase-confirm-dialog", inputs: ["itemName", "quantity", "saving"], outputs: ["cancel", "confirm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
23453
23743
  }
23454
23744
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: PreparingPurchaseComponent, decorators: [{
23455
23745
  type: Component,
@@ -23457,7 +23747,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
23457
23747
  CommonModule,
23458
23748
  PreparingPurchaseDialogComponent,
23459
23749
  PreparingPurchaseConfirmDialogComponent,
23460
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"pp-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"C\u1EA7n mua s\u1EAFm\">\n <header class=\"pp-header\">\n <button type=\"button\" class=\"pp-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"pp-header__title\">C\u1EA7n mua s\u1EAFm</h1>\n <span class=\"pp-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"pp-icon-btn\"\n (click)=\"load()\"\n [disabled]=\"loading()\"\n title=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n aria-label=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n >\n <i class=\"pi pi-refresh\" [class.pp-icon-btn__spin]=\"loading()\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n @if (errorMsg()) {\n <div class=\"pp-banner pp-banner--error\" role=\"alert\">\n <span>{{ errorMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n @if (successMsg()) {\n <div class=\"pp-banner pp-banner--success\" role=\"status\">\n <span>{{ successMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n <div class=\"pp-overview\" aria-label=\"T\u1ED5ng quan\">\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--amber\">{{ pendingItems().length }}</span>\n <span class=\"pp-overview__label\">Ch\u01B0a mua</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ highPriorityCount() }}</span>\n <span class=\"pp-overview__label\">\u01AFu ti\u00EAn cao</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ overdueCount() }}</span>\n <span class=\"pp-overview__label\">Qu\u00E1 h\u1EA1n</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--success\">{{ purchasedThisMonthCount() }}</span>\n <span class=\"pp-overview__label\">\u0110\u00E3 mua th\u00E1ng</span>\n </div>\n </div>\n\n @if (pendingItems().length > 0) {\n <div class=\"pp-priority-bar\" aria-hidden=\"true\">\n <div class=\"pp-priority-bar__track\">\n @for (segment of prioritySegments(); track segment.priority) {\n <span\n class=\"pp-priority-bar__segment\"\n [class.pp-priority-bar__segment--high]=\"segment.priority === 'high'\"\n [class.pp-priority-bar__segment--medium]=\"segment.priority === 'medium'\"\n [class.pp-priority-bar__segment--low]=\"segment.priority === 'low'\"\n [style.width.%]=\"segment.percent\"\n ></span>\n }\n </div>\n <div class=\"pp-priority-bar__legend\">\n <span>\u2191 Cao {{ priorityCounts().high }}</span>\n <span>\u2014 TB {{ priorityCounts().medium }}</span>\n <span>\u2193 Th\u1EA5p {{ priorityCounts().low }}</span>\n </div>\n </div>\n }\n\n <div class=\"pp-tabs\" role=\"tablist\" aria-label=\"Tr\u1EA1ng th\u00E1i mua\">\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'pending'\"\n [attr.aria-selected]=\"activeTab() === 'pending'\"\n (click)=\"setTab('pending')\"\n >\n C\u1EA7n mua <span class=\"pp-tabs__count\">{{ pendingItems().length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'purchased'\"\n [attr.aria-selected]=\"activeTab() === 'purchased'\"\n (click)=\"setTab('purchased')\"\n >\n \u0110\u00E3 mua <span class=\"pp-tabs__count\">{{ purchasedItems().length }}</span>\n </button>\n </div>\n\n @if (activeTab() === 'pending') {\n <div class=\"pp-filters\" role=\"group\" aria-label=\"L\u1ECDc theo \u01B0u ti\u00EAn\">\n @for (chip of priorityFilterOptions; track chip.id) {\n <button\n type=\"button\"\n class=\"pp-filter-chip\"\n [class.pp-filter-chip--active]=\"priorityFilter() === chip.id\"\n (click)=\"setPriorityFilter(chip.id)\"\n >\n {{ chip.label }}\n </button>\n }\n </div>\n }\n\n <div class=\"pp-list\">\n @if (activeTab() === 'pending') {\n @if (priorityFilter() === 'all') {\n @for (group of pendingPriorityGroups(); track group.priority) {\n @if (group.items.length > 0) {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"group.priority === 'high'\"\n [class.pp-group-label--medium]=\"group.priority === 'medium'\"\n [class.pp-group-label--low]=\"group.priority === 'low'\"\n >\n {{ priorityGroupHeading(group.priority) }}\n </p>\n @for (item of group.items; track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n }\n } @else {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"priorityFilter() === 'high'\"\n [class.pp-group-label--medium]=\"priorityFilter() === 'medium'\"\n [class.pp-group-label--low]=\"priorityFilter() === 'low'\"\n >\n {{ filteredPriorityHeading() }}\n </p>\n @for (item of filteredPendingItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n\n @if (filteredPendingItems().length === 0 && !loading()) {\n <p class=\"pp-empty\">Kh\u00F4ng c\u00F2n m\u00F3n n\u00E0o c\u1EA7n mua.</p>\n }\n } @else {\n @for (item of purchasedItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"purchasedRow; context: { $implicit: item }\" />\n } @empty {\n @if (!loading()) {\n <p class=\"pp-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o \u0111\u00E3 mua.</p>\n }\n }\n }\n\n @if (loading() && pendingItems().length === 0 && purchasedItems().length === 0) {\n <p class=\"pp-empty\">\u0110ang t\u1EA3i\u2026</p>\n }\n </div>\n\n <div class=\"pp-add-wrap\">\n <button type=\"button\" class=\"pp-add\" (click)=\"openAdd()\" [disabled]=\"saving()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n</section>\n\n<ng-template #pendingRow let-item>\n <article\n class=\"pp-row pp-row--pending pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n [class.pp-row--high]=\"item.priority === 'high'\"\n [class.pp-row--medium]=\"item.priority === 'medium'\"\n [class.pp-row--low]=\"item.priority === 'low'\"\n (click)=\"openPendingDetailItem(item)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPendingDetailItem(item)\"\n (keydown.space)=\"$event.preventDefault(); openPendingDetailItem(item)\"\n [attr.aria-label]=\"'Xem chi ti\u1EBFt ' + item.itemName\"\n >\n <span\n class=\"pp-priority\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n @if (rowSubtitle(item); as subtitle) {\n <p class=\"pp-row__desc\">{{ subtitle }}</p>\n }\n <p class=\"pp-row__meta\">\n Th\u00EAm {{ formatDate(item.createdDate) }}\n @if (item.createdBy) {\n \u00B7 {{ item.createdBy }}\n }\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__actions-col\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row__confirm\"\n (click)=\"openPurchaseConfirm(item)\"\n [disabled]=\"saving()\"\n title=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n aria-label=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"pp-row__menu-wrap\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPendingDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openEdit(item); rowMenuKey.set(null)\"\n >\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item pp-row-menu__item--danger\"\n role=\"menuitem\"\n (click)=\"askDelete(item, $event)\"\n >\n Xo\u00E1\n </button>\n </div>\n }\n </div>\n </div>\n </article>\n</ng-template>\n\n<ng-template #purchasedRow let-item>\n <article\n class=\"pp-row pp-row--purchased pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n (click)=\"openPurchaseDetail(item, $event)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPurchaseDetail(item, $event)\"\n (keydown.space)=\"$event.preventDefault(); openPurchaseDetailItem(item)\"\n [attr.aria-label]=\"'Xem l\u1ECBch s\u1EED mua ' + item.itemName\"\n >\n <span\n class=\"pp-priority pp-priority--muted\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n <p class=\"pp-row__desc\">\n @if (item.description) {\n {{ item.description }}\n } @else if (item.purchasedDate) {\n Mua {{ formatDate(item.purchasedDate) }}\n }\n </p>\n <p class=\"pp-row__meta\">\n @if (item.purchasedDate) {\n \u0110\u00E3 mua {{ formatDate(item.purchasedDate) }}\n }\n \u00B7 {{ purchaseCountForProduct(item.productId) }} l\u1EA7n\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__menu-wrap\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPurchaseDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n <span class=\"pp-row-menu__badge\">{{ purchaseCountForProduct(item.productId) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"duplicateFromPurchased(item, $event)\"\n >\n Mua l\u1EA1i\n </button>\n </div>\n }\n </div>\n </article>\n</ng-template>\n\n@if (isDialogOpen()) {\n <app-preparing-purchase-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [products]=\"products()\"\n [initialProductId]=\"dialogProductId()\"\n [initialProductName]=\"dialogProductName()\"\n [initialProductDescription]=\"dialogProductDescription()\"\n [initialAmount]=\"dialogAmount()\"\n [initialNote]=\"dialogNote()\"\n [initialAssignee]=\"dialogAssignee()\"\n [initialDueDate]=\"dialogDueDate()\"\n [initialPriority]=\"dialogPriority()\"\n [saving]=\"saving()\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSave($event)\"\n />\n}\n\n@if (purchaseConfirm()) {\n <app-preparing-purchase-confirm-dialog\n [itemName]=\"purchaseConfirm()?.itemName ?? ''\"\n [quantity]=\"purchaseConfirm()?.quantity ?? ''\"\n [saving]=\"saving()\"\n (cancel)=\"cancelPurchaseConfirm()\"\n (confirm)=\"onConfirmPurchase($event)\"\n />\n}\n\n@if (pendingDetailItem(); as detail) {\n <div class=\"pp-overlay pp-overlay--detail\" (click)=\"closePendingDetail()\" role=\"presentation\">\n <div\n class=\"pp-detail\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"detail.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-detail__hero\">\n <div class=\"pp-detail__hero-top\">\n <span\n class=\"pp-detail__priority\"\n [class.pp-detail__priority--high]=\"detail.priority === 'high'\"\n [class.pp-detail__priority--medium]=\"detail.priority === 'medium'\"\n [class.pp-detail__priority--low]=\"detail.priority === 'low'\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(detail.priority)\" aria-hidden=\"true\"></i>\n {{ priorityLabel(detail.priority) }}\n </span>\n <button type=\"button\" class=\"pp-icon-btn pp-icon-btn--soft\" (click)=\"closePendingDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <h2 class=\"pp-detail__name\">{{ detail.itemName }}</h2>\n <p class=\"pp-detail__meta\">\n <span>\n <i class=\"fa-solid fa-user\" aria-hidden=\"true\"></i>\n {{ detail.createdBy || 'guest' }}\n </span>\n <span>\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n {{ formatDateTime(detail.createdDate) }}\n </span>\n </p>\n </header>\n\n <div class=\"pp-detail__body\">\n <section class=\"pp-detail-card\" aria-label=\"Y\u00EAu c\u1EA7u mua\">\n <h3 class=\"pp-detail-card__title\">Y\u00EAu c\u1EA7u mua</h3>\n <dl class=\"pp-detail-kv\">\n <div class=\"pp-detail-kv__row\">\n <dt>S\u1ED1 l\u01B0\u1EE3ng</dt>\n <dd>{{ detail.quantity || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Ng\u01B0\u1EDDi ph\u1EE5 tr\u00E1ch</dt>\n <dd>{{ itemAssignee(detail) || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Th\u1EDDi h\u1EA1n mua</dt>\n <dd [class.pp-detail-kv__value--overdue]=\"isItemOverdue(detail)\">\n {{ itemDueDate(detail) || '\u2014' }}\n </dd>\n </div>\n </dl>\n </section>\n\n @if (detail.description?.trim()) {\n <section class=\"pp-detail-card\" aria-label=\"M\u00F4 t\u1EA3 s\u1EA3n ph\u1EA9m\">\n <h3 class=\"pp-detail-card__title\">M\u00F4 t\u1EA3</h3>\n <p class=\"pp-detail-card__text\">{{ detail.description }}</p>\n </section>\n }\n\n @if (itemNoteText(detail); as noteText) {\n <section class=\"pp-detail-card\" aria-label=\"Ghi ch\u00FA\">\n <h3 class=\"pp-detail-card__title\">Ghi ch\u00FA</h3>\n <p class=\"pp-detail-card__text\">{{ noteText }}</p>\n </section>\n }\n\n <section class=\"pp-comments\" aria-label=\"B\u00ECnh lu\u1EADn\">\n <div class=\"pp-comments__head\">\n <h3 class=\"pp-comments__title\">B\u00ECnh lu\u1EADn</h3>\n <span class=\"pp-comments__count\">{{ detailComments().length }}</span>\n </div>\n\n <ul class=\"pp-comments__list\">\n @for (comment of detailComments(); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDateTime(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">\n @for (part of commentSegments(comment.text); track $index) {\n @if (part.type === 'link') {\n <a\n class=\"pp-comment__link\"\n [href]=\"part.href\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >{{ part.value }}</a>\n } @else {\n {{ part.value }}\n }\n }\n </p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn \u2014 th\u00EAm ghi ch\u00FA cho m\u1ECDi ng\u01B0\u1EDDi c\u00F9ng xem.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"commentDraft()\"\n (input)=\"onCommentDraft($any($event.target).value)\"\n [disabled]=\"commentSaving()\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn\u2026\"\n aria-label=\"N\u1ED9i dung b\u00ECnh lu\u1EADn\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitComment()\"\n [disabled]=\"commentSaving() || !commentDraft().trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (commentSaving()) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-paper-plane\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </div>\n\n <footer class=\"pp-detail__foot\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--success pp-btn--block\"\n (click)=\"openPurchaseConfirm(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 mua\n </button>\n <div class=\"pp-detail__foot-row\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--ghost\"\n (click)=\"openEdit(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--danger-soft\"\n (click)=\"askDeleteFromDetail(detail)\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n X\u00F3a\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePendingDetail()\">\n \u0110\u00F3ng\n </button>\n </div>\n </footer>\n </div>\n </div>\n}\n\n@if (purchaseDetailProductId() && purchaseDetailItem(); as detailItem) {\n <div class=\"pp-overlay\" (click)=\"closePurchaseDetail()\" role=\"presentation\">\n <div\n class=\"pp-history-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'L\u1ECBch s\u1EED mua ' + detailItem.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-history-sheet__head\">\n <div>\n <h2 class=\"pp-history-sheet__title\">{{ detailItem.itemName }}</h2>\n <p class=\"pp-history-sheet__sub\">\n \u0110\u00E3 mua {{ purchaseHistoryRows().length }} l\u1EA7n\n </p>\n </div>\n <button type=\"button\" class=\"pp-icon-btn\" (click)=\"closePurchaseDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"pp-history-sheet__body\">\n @for (row of purchaseHistoryRows(); track itemKey(row); let idx = $index) {\n <article class=\"pp-purchase-entry\">\n <header class=\"pp-purchase-entry__head\">\n <span class=\"pp-purchase-entry__index\">L\u1EA7n {{ purchaseHistoryRows().length - idx }}</span>\n <span class=\"pp-purchase-entry__date\">\n {{ formatDate(row.purchasedDate ?? row.createdDate) }}\n </span>\n @if (row.quantity) {\n <span class=\"pp-purchase-entry__qty\">{{ row.quantity }}</span>\n }\n </header>\n\n @if (displayPurchaseNote(row) !== '\u2014') {\n <p class=\"pp-purchase-entry__note\">{{ displayPurchaseNote(row) }}</p>\n }\n\n <section class=\"pp-comments pp-comments--nested\" aria-label=\"B\u00ECnh lu\u1EADn l\u1EA7n mua\">\n <h3 class=\"pp-comments__title\">\n B\u00ECnh lu\u1EADn\n <span class=\"pp-comments__count\">{{ commentsForRequest(row.requestId).length }}</span>\n </h3>\n\n <ul class=\"pp-comments__list\">\n @for (comment of commentsForRequest(row.requestId); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDate(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">{{ comment.text }}</p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"historyCommentDraft(row.requestId)\"\n (input)=\"onHistoryCommentDraft(row.requestId, $any($event.target).value)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId)\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y\u2026\"\n [attr.aria-label]=\"'B\u00ECnh lu\u1EADn l\u1EA7n ' + (purchaseHistoryRows().length - idx)\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitHistoryComment(row)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId) || !historyCommentDraft(row.requestId).trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (isHistoryCommentSaving(row.requestId)) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"pi pi-send\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </article>\n }\n </div>\n\n <footer class=\"pp-history-sheet__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePurchaseDetail()\">\u0110\u00F3ng</button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--primary\"\n (click)=\"duplicateFromPurchased(detailItem, $event); closePurchaseDetail()\"\n >\n Mua l\u1EA1i\n </button>\n </footer>\n </div>\n </div>\n}\n\n@if (deleteConfirm()) {\n <div class=\"pp-overlay\" (click)=\"cancelDelete()\" role=\"presentation\">\n <div\n class=\"pp-confirm\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Xo\u00E1 m\u00F3n\"\n (click)=\"$event.stopPropagation()\"\n >\n <h2 class=\"pp-confirm__title\">Xo\u00E1 m\u00F3n n\u00E0y?</h2>\n <p class=\"pp-confirm__body\">{{ deleteConfirm()?.itemName }}</p>\n <div class=\"pp-confirm__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"cancelDelete()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--danger\" (click)=\"confirmDelete()\" [disabled]=\"saving()\">\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n \u0110ang xo\u00E1\u2026\n } @else {\n Xo\u00E1\n }\n </button>\n </div>\n </div>\n </div>\n}\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;--success-soft: #ecfdf5;--success: #059669;--warning-soft: #fff7ed;--warning: #ea580c;--danger: #e11d48;--danger-soft: #fff1f2;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:pp-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes pp-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.pp-root{display:flex;flex-direction:column;flex:1;min-height:0}.pp-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.pp-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-back:hover{background:var(--primary-soft);color:var(--primary)}.pp-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15}.pp-header__spacer{flex:1}.pp-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.pp-icon-btn--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-icon-btn--success{color:var(--success)}.pp-icon-btn--success:hover{background:var(--success-soft);color:var(--success)}.pp-icon-btn__spin{animation:pp-spin .8s linear infinite}@keyframes pp-spin{to{transform:rotate(360deg)}}.pp-banner{margin:10px 12px 0;padding:10px 12px;border-radius:12px;display:flex;align-items:center;gap:8px;font-size:13px}.pp-banner--error{background:var(--danger-soft);color:var(--danger)}.pp-banner--success{background:var(--success-soft);color:var(--success)}.pp-banner__close{margin-left:auto;border:none;background:transparent;color:inherit;cursor:pointer}.pp-overview{flex-shrink:0;display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:10px 12px 0}.pp-overview__cell{background:var(--surface);border:1px solid var(--border);border-radius:0;padding:8px 4px;text-align:center}.pp-overview__value{display:block;font-size:16px;font-weight:700;line-height:1.1}.pp-overview__value--amber{color:var(--warning)}.pp-overview__value--danger{color:var(--danger)}.pp-overview__value--success{color:var(--success)}.pp-overview__label{display:block;margin-top:2px;font-size:8px;color:var(--muted-2);line-height:1.2}.pp-priority-bar{flex-shrink:0;padding:8px 12px 0}.pp-priority-bar__track{display:flex;height:5px;overflow:hidden;background:var(--border)}.pp-priority-bar__segment{height:100%}.pp-priority-bar__segment--high{background:var(--danger)}.pp-priority-bar__segment--medium{background:#eab308}.pp-priority-bar__segment--low{background:#22c55e}.pp-priority-bar__legend{display:flex;gap:10px;margin-top:4px;font-size:9px;color:var(--muted)}.pp-tabs{flex-shrink:0;display:flex;margin:10px 12px 0;border:1px solid var(--border);background:#f7f8fc}.pp-tabs__btn{flex:1;border:none;background:transparent;padding:8px 0;font-size:11px;font-weight:700;color:var(--muted);cursor:pointer;font-family:inherit}.pp-tabs__btn:first-child{border-right:1px solid var(--border)}.pp-tabs__btn--active{background:var(--surface);color:var(--text)}.pp-tabs__count{color:var(--muted-2);font-weight:700}.pp-filters{flex-shrink:0;display:flex;gap:4px;padding:8px 12px 0;overflow-x:auto}.pp-filter-chip{flex-shrink:0;border:1px solid var(--border);border-radius:0;background:var(--surface);color:var(--muted);font-size:10px;font-weight:600;padding:4px 8px;cursor:pointer;font-family:inherit}.pp-filter-chip--active{border-color:var(--primary);background:var(--primary-soft);color:var(--primary)}.pp-group-label{margin:4px 0 0;font-size:10px;font-weight:700;letter-spacing:.03em}.pp-group-label--high{color:var(--danger)}.pp-group-label--medium{color:#d97706}.pp-group-label--low{color:var(--success)}.pp-list{flex:1;overflow:auto;padding:8px 12px 12px;display:flex;flex-direction:column;gap:5px}.pp-priority{flex-shrink:0;width:18px;padding-top:2px;display:inline-flex;align-items:flex-start;justify-content:center;font-size:12px;font-weight:700}.pp-priority--high{color:#e11d48}.pp-priority--medium{color:#eab308}.pp-priority--low{color:#22c55e}.pp-priority--muted{opacity:.55}.pp-row{position:relative;display:flex;gap:6px;align-items:flex-start;padding:9px 10px;background:var(--surface);border:1px solid var(--border);border-radius:0}.pp-row--pending{border-left-width:3px}.pp-row--high{border-left-color:var(--danger)}.pp-row--medium{border-left-color:#eab308}.pp-row--low{border-left-color:#22c55e}.pp-row--purchased{opacity:.95;background:#fafbfc}.pp-row--clickable{cursor:pointer;transition:border-color .15s ease,background .15s ease}.pp-row--clickable:hover{border-color:color-mix(in srgb,var(--primary) 35%,var(--border));background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.pp-row--menu-open{z-index:10;overflow:visible}.pp-row--clickable{cursor:pointer;transition:background .15s ease}.pp-row--clickable:hover{background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:-2px}.pp-row__body{flex:1;min-width:0}.pp-row__top{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:2px}.pp-row__main{flex:1;min-width:0}.pp-row__title{margin:0;font-size:14px;font-weight:700;line-height:1.25;min-width:0}.pp-row--compact .pp-row__title{font-size:14px}.pp-row__desc{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pp-row__qty{flex-shrink:0;font-size:12px;color:var(--primary);font-weight:700;white-space:nowrap}.pp-row__note{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}.pp-row__meta{margin:4px 0 0;font-size:9px;color:var(--muted-2);min-width:0}.pp-row__comments{display:inline-flex;align-items:center;gap:3px;white-space:nowrap}.pp-row__comments i{font-size:9px}.pp-row__actions-col{display:flex;flex-direction:column;align-items:center;gap:6px;flex-shrink:0}.pp-row__purchase-note{margin:0 0 6px;font-size:13px;color:var(--success);line-height:1.4}.pp-row__purchase-note i{margin-right:4px;font-size:12px}.pp-row__confirm{flex-shrink:0;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--success);border-radius:0;background:var(--success-soft);color:var(--success);font-size:12px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-row__confirm:hover{background:var(--success);color:#fff}.pp-row__confirm:disabled{opacity:.6;cursor:not-allowed}.pp-row__menu-wrap{position:relative;flex-shrink:0;align-self:center}.pp-icon-btn--menu{color:var(--muted-2)}.pp-row-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:148px;background:var(--surface);border:1px solid var(--border);border-radius:0;box-shadow:0 8px 24px #0f172a1f;padding:4px;z-index:20}.pp-row-menu__item{width:100%;border:none;background:transparent;border-radius:0;padding:8px 10px;display:inline-flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--text);cursor:pointer;text-align:left;font-family:inherit}.pp-row-menu__item:not(:last-child){border-bottom:1px solid var(--border)}.pp-row-menu__item:hover{background:var(--primary-soft);color:var(--primary)}.pp-row-menu__item--danger{color:var(--danger)}.pp-row-menu__item--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-row-menu__badge{margin-left:auto;font-size:11px;font-weight:700;padding:2px 7px;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-empty{margin:12px 0;text-align:center;color:var(--muted-2);font-size:14px}.pp-empty--group{margin:8px 0 0;font-size:13px}.pp-add-wrap{flex-shrink:0;padding:12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px));background:var(--surface);border-top:1px solid var(--border)}.pp-add{width:100%;border:none;border-radius:0;padding:11px 16px;background:var(--primary);color:#fff;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer}.pp-add:disabled{opacity:.6;cursor:not-allowed}.pp-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));padding-bottom:calc(16px + env(safe-area-inset-bottom,0px))}.pp-overlay--detail{background:#0f172a61;padding:12px}.pp-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:0;padding:18px 16px}.pp-confirm__title{margin:0 0 8px;font-size:16px}.pp-confirm__body{margin:0 0 16px;color:var(--muted);font-size:14px}.pp-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.pp-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer}.pp-btn--ghost{background:transparent;color:var(--muted)}.pp-btn--danger{background:var(--danger);color:#fff}.pp-btn--primary{background:var(--primary);color:#fff}.pp-btn--success{background:var(--success-soft);border:1px solid var(--success);color:var(--success)}.pp-btn--block{width:100%}.pp-btn--danger-soft{background:color-mix(in srgb,var(--danger) 10%,var(--surface));border:1px solid color-mix(in srgb,var(--danger) 35%,var(--border));color:var(--danger)}.pp-icon-btn--soft{width:34px;height:34px;border-radius:10px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail{width:100%;max-width:420px;max-height:min(90vh,720px);background:var(--surface);border:1px solid var(--border);border-radius:16px;display:flex;flex-direction:column;overflow:hidden}.pp-detail__hero{padding:16px 16px 12px;border-bottom:1px solid color-mix(in srgb,var(--border) 70%,transparent)}.pp-detail__hero-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}.pp-detail__priority{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail__priority--high{background:color-mix(in srgb,var(--danger) 12%,white);color:var(--danger)}.pp-detail__priority--medium{background:color-mix(in srgb,#d97706 12%,white);color:#d97706}.pp-detail__priority--low{background:color-mix(in srgb,var(--success) 12%,white);color:var(--success)}.pp-detail__priority .fa-solid{font-size:10px}.pp-detail__name{margin:0 0 8px;font-size:20px;font-weight:800;line-height:1.25;letter-spacing:-.02em;color:var(--text);overflow-wrap:anywhere}.pp-detail__meta{margin:0;display:flex;flex-wrap:wrap;gap:12px;font-size:12px;font-weight:600;color:var(--muted)}.pp-detail__meta span{display:inline-flex;align-items:center;gap:6px}.pp-detail__meta .fa-solid{font-size:11px;opacity:.75}.pp-detail__body{flex:1;overflow:auto;padding:14px 16px 16px;display:flex;flex-direction:column;gap:10px}.pp-detail__foot{display:flex;flex-direction:column;gap:8px;padding:12px 16px 16px;border-top:1px solid color-mix(in srgb,var(--border) 70%,transparent);background:color-mix(in srgb,var(--surface-2, #f8fafc) 50%,var(--surface))}.pp-detail__foot-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.pp-detail__foot-row .pp-btn{width:100%;padding:9px 8px;font-size:13px}.pp-detail-card{padding:12px 14px;border-radius:14px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent)}.pp-detail-card__title{margin:0 0 8px;font-size:12px;font-weight:700;color:var(--muted)}.pp-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:var(--text);overflow-wrap:anywhere}.pp-detail-kv{margin:0}.pp-detail-kv__row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:5px 0}.pp-detail-kv__row:not(:last-child){border-bottom:1px solid color-mix(in srgb,var(--border) 55%,transparent)}.pp-detail-kv__row dt{margin:0;font-size:12px;font-weight:500;color:var(--muted)}.pp-detail-kv__row dd{margin:0;font-size:13px;font-weight:700;color:var(--text);text-align:right;overflow-wrap:anywhere}.pp-detail-kv__value--overdue{color:var(--danger)}.pp-comments{margin-top:4px}.pp-comments__head{display:flex;align-items:center;gap:8px;margin-bottom:10px}.pp-comments__title{margin:0;font-size:13px;font-weight:700;color:var(--text)}.pp-comments__count{font-size:11px;font-weight:700;min-width:22px;height:22px;padding:0 7px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-comments__list{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:8px}.pp-comments__empty{font-size:12px;line-height:1.45;color:var(--muted-2);padding:10px 12px;border-radius:12px;background:var(--surface-2, #f8fafc);border:1px dashed color-mix(in srgb,var(--border) 90%,transparent)}.pp-comments__composer{display:flex;align-items:flex-end;gap:8px;margin-top:12px}.pp-comments__input{flex:1;min-width:0;resize:vertical;min-height:44px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--border) 90%,transparent);border-radius:12px;background:var(--surface);color:var(--text);font-size:13px;font-family:inherit;outline:none;line-height:1.4}.pp-comments__input:focus{border-color:var(--primary)}.pp-comments__send{flex-shrink:0;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:12px;background:var(--primary);color:#fff;font-size:14px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-comments__send:hover:not(:disabled){filter:brightness(1.05)}.pp-comments__send:disabled{opacity:.5;cursor:not-allowed}.pp-comment{border-radius:12px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent);padding:10px 12px}.pp-comment__head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:6px}.pp-comment__author{font-size:12px;font-weight:700;color:var(--text)}.pp-comment__date{font-size:10px;font-weight:600;color:var(--muted-2);font-variant-numeric:tabular-nums}.pp-comment__text{margin:0;font-size:13px;color:var(--muted);line-height:1.45;overflow-wrap:anywhere;white-space:pre-wrap}.pp-comment__link{color:var(--primary);text-decoration:underline;text-underline-offset:2px;word-break:break-all}.pp-comment__link:hover{filter:brightness(.92)}.pp-purchase-entry{border:1px solid var(--border);background:#fafbfc;padding:12px;margin-bottom:10px}.pp-purchase-entry:last-child{margin-bottom:0}.pp-purchase-entry__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;margin-bottom:8px}.pp-purchase-entry__index{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.03em;color:var(--primary)}.pp-purchase-entry__date{font-size:13px;font-weight:700;color:var(--text)}.pp-purchase-entry__qty{margin-left:auto;font-size:12px;font-weight:700;color:var(--muted)}.pp-purchase-entry__note{margin:0 0 10px;font-size:12px;line-height:1.4;color:var(--muted);overflow-wrap:anywhere}.pp-comments--nested{margin-top:0;padding-top:0;border-top:none}.pp-history-sheet{width:min(100%,520px);max-height:min(92vh,720px);margin:0 auto;background:var(--surface);border-radius:0;display:flex;flex-direction:column;overflow:hidden}.pp-history-sheet__head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:16px;border-bottom:1px solid var(--border)}.pp-history-sheet__title{margin:0;font-size:17px;font-weight:700}.pp-history-sheet__sub{margin:4px 0 0;font-size:13px;color:var(--muted)}.pp-history-sheet__body{flex:1;overflow:auto;padding:12px 16px}.pp-history-sheet__foot{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}.pp-history-table{width:100%;border-collapse:collapse;font-size:13px}.pp-history-table th,.pp-history-table td{padding:10px 8px;text-align:left;border-bottom:1px solid var(--border);vertical-align:top}.pp-history-table th{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--muted-2);background:#f8fafc;position:sticky;top:0}.pp-history-table tbody tr:last-child td{border-bottom:none}.pp-history-table td:first-child{width:36px;color:var(--muted-2);font-weight:700}.pp-history-table td:nth-child(3){white-space:nowrap}\n"] }]
23750
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"pp-root\" role=\"dialog\" aria-modal=\"true\" aria-label=\"C\u1EA7n mua s\u1EAFm\">\n <header class=\"pp-header\">\n <button type=\"button\" class=\"pp-back\" (click)=\"back.emit()\" aria-label=\"Quay l\u1EA1i\">\n <i class=\"pi pi-chevron-left\" aria-hidden=\"true\"></i>\n </button>\n <h1 class=\"pp-header__title\">C\u1EA7n mua s\u1EAFm</h1>\n <span class=\"pp-header__spacer\"></span>\n <button\n type=\"button\"\n class=\"pp-icon-btn\"\n (click)=\"load(true)\"\n [disabled]=\"loading()\"\n title=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n aria-label=\"L\u00E0m m\u1EDBi danh s\u00E1ch\"\n >\n <i class=\"pi pi-refresh\" [class.pp-icon-btn__spin]=\"loading()\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n @if (errorMsg()) {\n <div class=\"pp-banner pp-banner--error\" role=\"alert\">\n <span>{{ errorMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n @if (successMsg()) {\n <div class=\"pp-banner pp-banner--success\" role=\"status\">\n <span>{{ successMsg() }}</span>\n <button type=\"button\" class=\"pp-banner__close\" (click)=\"clearMessages()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n }\n\n <div class=\"pp-overview\" aria-label=\"T\u1ED5ng quan\">\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--amber\">{{ pendingItems().length }}</span>\n <span class=\"pp-overview__label\">Ch\u01B0a mua</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ highPriorityCount() }}</span>\n <span class=\"pp-overview__label\">\u01AFu ti\u00EAn cao</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--danger\">{{ overdueCount() }}</span>\n <span class=\"pp-overview__label\">Qu\u00E1 h\u1EA1n</span>\n </div>\n <div class=\"pp-overview__cell\">\n <span class=\"pp-overview__value pp-overview__value--success\">{{ purchasedThisMonthCount() }}</span>\n <span class=\"pp-overview__label\">\u0110\u00E3 mua th\u00E1ng</span>\n </div>\n </div>\n\n @if (pendingItems().length > 0) {\n <div class=\"pp-priority-bar\" aria-hidden=\"true\">\n <div class=\"pp-priority-bar__track\">\n @for (segment of prioritySegments(); track segment.priority) {\n <span\n class=\"pp-priority-bar__segment\"\n [class.pp-priority-bar__segment--high]=\"segment.priority === 'high'\"\n [class.pp-priority-bar__segment--medium]=\"segment.priority === 'medium'\"\n [class.pp-priority-bar__segment--low]=\"segment.priority === 'low'\"\n [style.width.%]=\"segment.percent\"\n ></span>\n }\n </div>\n <div class=\"pp-priority-bar__legend\">\n <span>\u2191 Cao {{ priorityCounts().high }}</span>\n <span>\u2014 TB {{ priorityCounts().medium }}</span>\n <span>\u2193 Th\u1EA5p {{ priorityCounts().low }}</span>\n </div>\n </div>\n }\n\n <div class=\"pp-tabs\" role=\"tablist\" aria-label=\"Tr\u1EA1ng th\u00E1i mua\">\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'pending'\"\n [attr.aria-selected]=\"activeTab() === 'pending'\"\n (click)=\"setTab('pending')\"\n >\n C\u1EA7n mua <span class=\"pp-tabs__count\">{{ pendingItems().length }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-tabs__btn\"\n role=\"tab\"\n [class.pp-tabs__btn--active]=\"activeTab() === 'purchased'\"\n [attr.aria-selected]=\"activeTab() === 'purchased'\"\n (click)=\"setTab('purchased')\"\n >\n \u0110\u00E3 mua <span class=\"pp-tabs__count\">{{ purchasedItems().length }}</span>\n </button>\n </div>\n\n @if (activeTab() === 'pending') {\n <div class=\"pp-filters\" role=\"group\" aria-label=\"L\u1ECDc theo \u01B0u ti\u00EAn\">\n @for (chip of priorityFilterOptions; track chip.id) {\n <button\n type=\"button\"\n class=\"pp-filter-chip\"\n [class.pp-filter-chip--active]=\"priorityFilter() === chip.id\"\n (click)=\"setPriorityFilter(chip.id)\"\n >\n {{ chip.label }}\n </button>\n }\n </div>\n }\n\n <div class=\"pp-list\">\n @if (activeTab() === 'pending') {\n @if (priorityFilter() === 'all') {\n @for (group of pendingPriorityGroups(); track group.priority) {\n @if (group.items.length > 0) {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"group.priority === 'high'\"\n [class.pp-group-label--medium]=\"group.priority === 'medium'\"\n [class.pp-group-label--low]=\"group.priority === 'low'\"\n >\n {{ priorityGroupHeading(group.priority) }}\n </p>\n @for (item of group.items; track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n }\n } @else {\n <p\n class=\"pp-group-label\"\n [class.pp-group-label--high]=\"priorityFilter() === 'high'\"\n [class.pp-group-label--medium]=\"priorityFilter() === 'medium'\"\n [class.pp-group-label--low]=\"priorityFilter() === 'low'\"\n >\n {{ filteredPriorityHeading() }}\n </p>\n @for (item of filteredPendingItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"pendingRow; context: { $implicit: item }\" />\n }\n }\n\n @if (filteredPendingItems().length === 0 && !loading()) {\n <p class=\"pp-empty\">Kh\u00F4ng c\u00F2n m\u00F3n n\u00E0o c\u1EA7n mua.</p>\n }\n } @else {\n @for (item of purchasedItems(); track itemKey(item)) {\n <ng-container *ngTemplateOutlet=\"purchasedRow; context: { $implicit: item }\" />\n } @empty {\n @if (!loading()) {\n <p class=\"pp-empty\">Ch\u01B0a c\u00F3 m\u00F3n n\u00E0o \u0111\u00E3 mua.</p>\n }\n }\n }\n\n @if (loading() && pendingItems().length === 0 && purchasedItems().length === 0) {\n <p class=\"pp-empty\">\u0110ang t\u1EA3i\u2026</p>\n }\n </div>\n\n <div class=\"pp-add-wrap\">\n <button type=\"button\" class=\"pp-add\" (click)=\"openAdd()\" [disabled]=\"saving()\">\n <i class=\"pi pi-plus\" aria-hidden=\"true\"></i>\n <span>Th\u00EAm m\u00F3n</span>\n </button>\n </div>\n</section>\n\n<ng-template #pendingRow let-item>\n <article\n class=\"pp-row pp-row--pending pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n [class.pp-row--high]=\"item.priority === 'high'\"\n [class.pp-row--medium]=\"item.priority === 'medium'\"\n [class.pp-row--low]=\"item.priority === 'low'\"\n (click)=\"openPendingDetailItem(item)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPendingDetailItem(item)\"\n (keydown.space)=\"$event.preventDefault(); openPendingDetailItem(item)\"\n [attr.aria-label]=\"'Xem chi ti\u1EBFt ' + item.itemName\"\n >\n <span\n class=\"pp-priority\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n @if (rowSubtitle(item); as subtitle) {\n <p class=\"pp-row__desc\">{{ subtitle }}</p>\n }\n <p class=\"pp-row__meta\">\n Th\u00EAm {{ formatDate(item.createdDate) }}\n @if (item.createdBy) {\n \u00B7 {{ item.createdBy }}\n }\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__actions-col\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row__confirm\"\n (click)=\"openPurchaseConfirm(item)\"\n [disabled]=\"saving()\"\n title=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n aria-label=\"\u0110\u00E1nh d\u1EA5u \u0111\u00E3 mua\"\n >\n <i class=\"pi pi-check\" aria-hidden=\"true\"></i>\n </button>\n <div class=\"pp-row__menu-wrap\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPendingDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openEdit(item); rowMenuKey.set(null)\"\n >\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item pp-row-menu__item--danger\"\n role=\"menuitem\"\n (click)=\"askDelete(item, $event)\"\n >\n Xo\u00E1\n </button>\n </div>\n }\n </div>\n </div>\n </article>\n</ng-template>\n\n<ng-template #purchasedRow let-item>\n <article\n class=\"pp-row pp-row--purchased pp-row--clickable\"\n [class.pp-row--menu-open]=\"rowMenuKey() === itemKey(item)\"\n (click)=\"openPurchaseDetail(item, $event)\"\n role=\"button\"\n tabindex=\"0\"\n (keydown.enter)=\"openPurchaseDetail(item, $event)\"\n (keydown.space)=\"$event.preventDefault(); openPurchaseDetailItem(item)\"\n [attr.aria-label]=\"'Xem l\u1ECBch s\u1EED mua ' + item.itemName\"\n >\n <span\n class=\"pp-priority pp-priority--muted\"\n [class.pp-priority--high]=\"item.priority === 'high'\"\n [class.pp-priority--medium]=\"item.priority === 'medium'\"\n [class.pp-priority--low]=\"item.priority === 'low'\"\n [attr.title]=\"'\u01AFu ti\u00EAn ' + priorityLabel(item.priority)\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(item.priority)\" aria-hidden=\"true\"></i>\n </span>\n\n <div class=\"pp-row__body\">\n <div class=\"pp-row__top\">\n <h2 class=\"pp-row__title\">{{ item.itemName }}</h2>\n @if (item.quantity) {\n <span class=\"pp-row__qty\">{{ item.quantity }}</span>\n }\n </div>\n <p class=\"pp-row__desc\">\n @if (item.description) {\n {{ item.description }}\n } @else if (item.purchasedDate) {\n Mua {{ formatDate(item.purchasedDate) }}\n }\n </p>\n <p class=\"pp-row__meta\">\n @if (item.purchasedDate) {\n \u0110\u00E3 mua {{ formatDate(item.purchasedDate) }}\n }\n \u00B7 {{ purchaseCountForProduct(item.productId) }} l\u1EA7n\n <span class=\"pp-row__comments\" [attr.title]=\"commentCountForRequest(item.requestId) + ' b\u00ECnh lu\u1EADn'\">\n \u00B7 <i class=\"pi pi-comment\" aria-hidden=\"true\"></i>\n {{ commentCountForRequest(item.requestId) }}\n </span>\n </p>\n </div>\n\n <div class=\"pp-row__menu-wrap\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-icon-btn pp-icon-btn--menu\"\n (click)=\"toggleRowMenu(item, $event)\"\n title=\"Tu\u1EF3 ch\u1ECDn\"\n aria-label=\"Tu\u1EF3 ch\u1ECDn\"\n aria-haspopup=\"menu\"\n [attr.aria-expanded]=\"rowMenuKey() === itemKey(item)\"\n >\n <i class=\"pi pi-ellipsis-v\" aria-hidden=\"true\"></i>\n </button>\n @if (rowMenuKey() === itemKey(item)) {\n <div class=\"pp-row-menu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"openPurchaseDetail(item, $event)\"\n >\n Xem chi ti\u1EBFt\n <span class=\"pp-row-menu__badge\">{{ purchaseCountForProduct(item.productId) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"pp-row-menu__item\"\n role=\"menuitem\"\n (click)=\"duplicateFromPurchased(item, $event)\"\n >\n Mua l\u1EA1i\n </button>\n </div>\n }\n </div>\n </article>\n</ng-template>\n\n@if (isDialogOpen()) {\n <app-preparing-purchase-dialog\n [heading]=\"dialogHeading()\"\n [mode]=\"dialogMode()\"\n [products]=\"products()\"\n [initialProductId]=\"dialogProductId()\"\n [initialProductName]=\"dialogProductName()\"\n [initialProductDescription]=\"dialogProductDescription()\"\n [initialAmount]=\"dialogAmount()\"\n [initialNote]=\"dialogNote()\"\n [initialAssignee]=\"dialogAssignee()\"\n [initialDueDate]=\"dialogDueDate()\"\n [initialPriority]=\"dialogPriority()\"\n [saving]=\"saving()\"\n (cancel)=\"closeDialog()\"\n (save)=\"onSave($event)\"\n />\n}\n\n@if (purchaseConfirm()) {\n <app-preparing-purchase-confirm-dialog\n [itemName]=\"purchaseConfirm()?.itemName ?? ''\"\n [quantity]=\"purchaseConfirm()?.quantity ?? ''\"\n [saving]=\"saving()\"\n (cancel)=\"cancelPurchaseConfirm()\"\n (confirm)=\"onConfirmPurchase($event)\"\n />\n}\n\n@if (pendingDetailItem(); as detail) {\n <div class=\"pp-overlay pp-overlay--detail\" (click)=\"closePendingDetail()\" role=\"presentation\">\n <div\n class=\"pp-detail\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"detail.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-detail__hero\">\n <div class=\"pp-detail__hero-top\">\n <span\n class=\"pp-detail__priority\"\n [class.pp-detail__priority--high]=\"detail.priority === 'high'\"\n [class.pp-detail__priority--medium]=\"detail.priority === 'medium'\"\n [class.pp-detail__priority--low]=\"detail.priority === 'low'\"\n >\n <i class=\"fa-solid\" [ngClass]=\"priorityIcon(detail.priority)\" aria-hidden=\"true\"></i>\n {{ priorityLabel(detail.priority) }}\n </span>\n <button type=\"button\" class=\"pp-icon-btn pp-icon-btn--soft\" (click)=\"closePendingDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <h2 class=\"pp-detail__name\">{{ detail.itemName }}</h2>\n <p class=\"pp-detail__meta\">\n <span>\n <i class=\"fa-solid fa-user\" aria-hidden=\"true\"></i>\n {{ detail.createdBy || 'guest' }}\n </span>\n <span>\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n {{ formatDateTime(detail.createdDate) }}\n </span>\n </p>\n </header>\n\n <div class=\"pp-detail__body\">\n <section class=\"pp-detail-card\" aria-label=\"Y\u00EAu c\u1EA7u mua\">\n <h3 class=\"pp-detail-card__title\">Y\u00EAu c\u1EA7u mua</h3>\n <dl class=\"pp-detail-kv\">\n <div class=\"pp-detail-kv__row\">\n <dt>S\u1ED1 l\u01B0\u1EE3ng</dt>\n <dd>{{ detail.quantity || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Ng\u01B0\u1EDDi ph\u1EE5 tr\u00E1ch</dt>\n <dd>{{ itemAssignee(detail) || '\u2014' }}</dd>\n </div>\n <div class=\"pp-detail-kv__row\">\n <dt>Th\u1EDDi h\u1EA1n mua</dt>\n <dd [class.pp-detail-kv__value--overdue]=\"isItemOverdue(detail)\">\n {{ itemDueDate(detail) || '\u2014' }}\n </dd>\n </div>\n </dl>\n </section>\n\n @if (detail.description?.trim()) {\n <section class=\"pp-detail-card\" aria-label=\"M\u00F4 t\u1EA3 s\u1EA3n ph\u1EA9m\">\n <h3 class=\"pp-detail-card__title\">M\u00F4 t\u1EA3</h3>\n <p class=\"pp-detail-card__text\">{{ detail.description }}</p>\n </section>\n }\n\n @if (itemNoteText(detail); as noteText) {\n <section class=\"pp-detail-card\" aria-label=\"Ghi ch\u00FA\">\n <h3 class=\"pp-detail-card__title\">Ghi ch\u00FA</h3>\n <p class=\"pp-detail-card__text\">{{ noteText }}</p>\n </section>\n }\n\n <section class=\"pp-comments\" aria-label=\"B\u00ECnh lu\u1EADn\">\n <div class=\"pp-comments__head\">\n <h3 class=\"pp-comments__title\">B\u00ECnh lu\u1EADn</h3>\n <span class=\"pp-comments__count\">{{ detailComments().length }}</span>\n </div>\n\n <ul class=\"pp-comments__list\">\n @for (comment of detailComments(); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDateTime(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">\n @for (part of commentSegments(comment.text); track $index) {\n @if (part.type === 'link') {\n <a\n class=\"pp-comment__link\"\n [href]=\"part.href\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n (click)=\"$event.stopPropagation()\"\n >{{ part.value }}</a>\n } @else {\n {{ part.value }}\n }\n }\n </p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn \u2014 th\u00EAm ghi ch\u00FA cho m\u1ECDi ng\u01B0\u1EDDi c\u00F9ng xem.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"commentDraft()\"\n (input)=\"onCommentDraft($any($event.target).value)\"\n [disabled]=\"commentSaving()\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn\u2026\"\n aria-label=\"N\u1ED9i dung b\u00ECnh lu\u1EADn\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitComment()\"\n [disabled]=\"commentSaving() || !commentDraft().trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (commentSaving()) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-paper-plane\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </div>\n\n <footer class=\"pp-detail__foot\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--success pp-btn--block\"\n (click)=\"openPurchaseConfirm(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n \u0110\u00E3 mua\n </button>\n <div class=\"pp-detail__foot-row\">\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--ghost\"\n (click)=\"openEdit(detail); closePendingDetail()\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-pencil\" aria-hidden=\"true\"></i>\n S\u1EEDa\n </button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--danger-soft\"\n (click)=\"askDeleteFromDetail(detail)\"\n [disabled]=\"saving()\"\n >\n <i class=\"fa-solid fa-trash\" aria-hidden=\"true\"></i>\n X\u00F3a\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePendingDetail()\">\n \u0110\u00F3ng\n </button>\n </div>\n </footer>\n </div>\n </div>\n}\n\n@if (purchaseDetailProductId() && purchaseDetailItem(); as detailItem) {\n <div class=\"pp-overlay\" (click)=\"closePurchaseDetail()\" role=\"presentation\">\n <div\n class=\"pp-history-sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"'L\u1ECBch s\u1EED mua ' + detailItem.itemName\"\n (click)=\"$event.stopPropagation()\"\n >\n <header class=\"pp-history-sheet__head\">\n <div>\n <h2 class=\"pp-history-sheet__title\">{{ detailItem.itemName }}</h2>\n <p class=\"pp-history-sheet__sub\">\n \u0110\u00E3 mua {{ purchaseHistoryRows().length }} l\u1EA7n\n </p>\n </div>\n <button type=\"button\" class=\"pp-icon-btn\" (click)=\"closePurchaseDetail()\" aria-label=\"\u0110\u00F3ng\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </header>\n\n <div class=\"pp-history-sheet__body\">\n @for (row of purchaseHistoryRows(); track itemKey(row); let idx = $index) {\n <article class=\"pp-purchase-entry\">\n <header class=\"pp-purchase-entry__head\">\n <span class=\"pp-purchase-entry__index\">L\u1EA7n {{ purchaseHistoryRows().length - idx }}</span>\n <span class=\"pp-purchase-entry__date\">\n {{ formatDate(row.purchasedDate ?? row.createdDate) }}\n </span>\n @if (row.quantity) {\n <span class=\"pp-purchase-entry__qty\">{{ row.quantity }}</span>\n }\n </header>\n\n @if (displayPurchaseNote(row) !== '\u2014') {\n <p class=\"pp-purchase-entry__note\">{{ displayPurchaseNote(row) }}</p>\n }\n\n <section class=\"pp-comments pp-comments--nested\" aria-label=\"B\u00ECnh lu\u1EADn l\u1EA7n mua\">\n <h3 class=\"pp-comments__title\">\n B\u00ECnh lu\u1EADn\n <span class=\"pp-comments__count\">{{ commentsForRequest(row.requestId).length }}</span>\n </h3>\n\n <ul class=\"pp-comments__list\">\n @for (comment of commentsForRequest(row.requestId); track comment.id) {\n <li class=\"pp-comment\">\n <div class=\"pp-comment__head\">\n <span class=\"pp-comment__author\">{{ comment.createdBy || 'guest' }}</span>\n <span class=\"pp-comment__date\">{{ formatDate(comment.createdDate) }}</span>\n </div>\n <p class=\"pp-comment__text\">{{ comment.text }}</p>\n </li>\n } @empty {\n <li class=\"pp-comments__empty\">Ch\u01B0a c\u00F3 b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y.</li>\n }\n </ul>\n\n <div class=\"pp-comments__composer\">\n <textarea\n class=\"pp-comments__input\"\n rows=\"2\"\n [value]=\"historyCommentDraft(row.requestId)\"\n (input)=\"onHistoryCommentDraft(row.requestId, $any($event.target).value)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId)\"\n placeholder=\"Th\u00EAm b\u00ECnh lu\u1EADn cho l\u1EA7n mua n\u00E0y\u2026\"\n [attr.aria-label]=\"'B\u00ECnh lu\u1EADn l\u1EA7n ' + (purchaseHistoryRows().length - idx)\"\n ></textarea>\n <button\n type=\"button\"\n class=\"pp-comments__send\"\n (click)=\"submitHistoryComment(row)\"\n [disabled]=\"isHistoryCommentSaving(row.requestId) || !historyCommentDraft(row.requestId).trim()\"\n title=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n aria-label=\"G\u1EEDi b\u00ECnh lu\u1EADn\"\n >\n @if (isHistoryCommentSaving(row.requestId)) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"pi pi-send\" aria-hidden=\"true\"></i>\n }\n </button>\n </div>\n </section>\n </article>\n }\n </div>\n\n <footer class=\"pp-history-sheet__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"closePurchaseDetail()\">\u0110\u00F3ng</button>\n <button\n type=\"button\"\n class=\"pp-btn pp-btn--primary\"\n (click)=\"duplicateFromPurchased(detailItem, $event); closePurchaseDetail()\"\n >\n Mua l\u1EA1i\n </button>\n </footer>\n </div>\n </div>\n}\n\n@if (deleteConfirm()) {\n <div class=\"pp-overlay\" (click)=\"cancelDelete()\" role=\"presentation\">\n <div\n class=\"pp-confirm\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-label=\"Xo\u00E1 m\u00F3n\"\n (click)=\"$event.stopPropagation()\"\n >\n <h2 class=\"pp-confirm__title\">Xo\u00E1 m\u00F3n n\u00E0y?</h2>\n <p class=\"pp-confirm__body\">{{ deleteConfirm()?.itemName }}</p>\n <div class=\"pp-confirm__foot\">\n <button type=\"button\" class=\"pp-btn pp-btn--ghost\" (click)=\"cancelDelete()\" [disabled]=\"saving()\">\n Hu\u1EF7\n </button>\n <button type=\"button\" class=\"pp-btn pp-btn--danger\" (click)=\"confirmDelete()\" [disabled]=\"saving()\">\n @if (saving()) {\n <i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i>\n \u0110ang xo\u00E1\u2026\n } @else {\n Xo\u00E1\n }\n </button>\n </div>\n </div>\n </div>\n}\n", styles: [":host{--bg: #f6f7fb;--surface: #ffffff;--border: #e6e8ef;--primary: #5b6cff;--primary-soft: #eef0ff;--text: #0f172a;--muted: #475569;--muted-2: #94a3b8;--success-soft: #ecfdf5;--success: #059669;--warning-soft: #fff7ed;--warning: #ea580c;--danger: #e11d48;--danger-soft: #fff1f2;position:fixed;inset:0;z-index:200;background:var(--bg);display:flex;flex-direction:column;color:var(--text);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;animation:pp-slide .22s cubic-bezier(.22,1,.36,1)}@keyframes pp-slide{0%{transform:translate(8px);opacity:0}to{transform:translate(0);opacity:1}}.pp-root{display:flex;flex-direction:column;flex:1;min-height:0}.pp-header{flex-shrink:0;display:flex;align-items:center;gap:6px;padding:8px 10px;padding-top:calc(8px + env(safe-area-inset-top,0px));background:var(--surface);border-bottom:1px solid var(--border)}.pp-back{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-back:hover{background:var(--primary-soft);color:var(--primary)}.pp-header__title{margin:0;font-size:16px;font-weight:700;line-height:1.15}.pp-header__spacer{flex:1}.pp-icon-btn{width:32px;height:32px;border:none;background:transparent;border-radius:9px;color:var(--muted);font-size:15px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;-webkit-tap-highlight-color:transparent}.pp-icon-btn:hover{background:var(--primary-soft);color:var(--primary)}.pp-icon-btn--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-icon-btn--success{color:var(--success)}.pp-icon-btn--success:hover{background:var(--success-soft);color:var(--success)}.pp-icon-btn__spin{animation:pp-spin .8s linear infinite}@keyframes pp-spin{to{transform:rotate(360deg)}}.pp-banner{margin:10px 12px 0;padding:10px 12px;border-radius:12px;display:flex;align-items:center;gap:8px;font-size:13px}.pp-banner--error{background:var(--danger-soft);color:var(--danger)}.pp-banner--success{background:var(--success-soft);color:var(--success)}.pp-banner__close{margin-left:auto;border:none;background:transparent;color:inherit;cursor:pointer}.pp-overview{flex-shrink:0;display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:10px 12px 0}.pp-overview__cell{background:var(--surface);border:1px solid var(--border);border-radius:0;padding:8px 4px;text-align:center}.pp-overview__value{display:block;font-size:16px;font-weight:700;line-height:1.1}.pp-overview__value--amber{color:var(--warning)}.pp-overview__value--danger{color:var(--danger)}.pp-overview__value--success{color:var(--success)}.pp-overview__label{display:block;margin-top:2px;font-size:8px;color:var(--muted-2);line-height:1.2}.pp-priority-bar{flex-shrink:0;padding:8px 12px 0}.pp-priority-bar__track{display:flex;height:5px;overflow:hidden;background:var(--border)}.pp-priority-bar__segment{height:100%}.pp-priority-bar__segment--high{background:var(--danger)}.pp-priority-bar__segment--medium{background:#eab308}.pp-priority-bar__segment--low{background:#22c55e}.pp-priority-bar__legend{display:flex;gap:10px;margin-top:4px;font-size:9px;color:var(--muted)}.pp-tabs{flex-shrink:0;display:flex;margin:10px 12px 0;border:1px solid var(--border);background:#f7f8fc}.pp-tabs__btn{flex:1;border:none;background:transparent;padding:8px 0;font-size:11px;font-weight:700;color:var(--muted);cursor:pointer;font-family:inherit}.pp-tabs__btn:first-child{border-right:1px solid var(--border)}.pp-tabs__btn--active{background:var(--surface);color:var(--text)}.pp-tabs__count{color:var(--muted-2);font-weight:700}.pp-filters{flex-shrink:0;display:flex;gap:4px;padding:8px 12px 0;overflow-x:auto}.pp-filter-chip{flex-shrink:0;border:1px solid var(--border);border-radius:0;background:var(--surface);color:var(--muted);font-size:10px;font-weight:600;padding:4px 8px;cursor:pointer;font-family:inherit}.pp-filter-chip--active{border-color:var(--primary);background:var(--primary-soft);color:var(--primary)}.pp-group-label{margin:4px 0 0;font-size:10px;font-weight:700;letter-spacing:.03em}.pp-group-label--high{color:var(--danger)}.pp-group-label--medium{color:#d97706}.pp-group-label--low{color:var(--success)}.pp-list{flex:1;overflow:auto;padding:8px 12px 12px;display:flex;flex-direction:column;gap:5px}.pp-priority{flex-shrink:0;width:18px;padding-top:2px;display:inline-flex;align-items:flex-start;justify-content:center;font-size:12px;font-weight:700}.pp-priority--high{color:#e11d48}.pp-priority--medium{color:#eab308}.pp-priority--low{color:#22c55e}.pp-priority--muted{opacity:.55}.pp-row{position:relative;display:flex;gap:6px;align-items:flex-start;padding:9px 10px;background:var(--surface);border:1px solid var(--border);border-radius:0}.pp-row--pending{border-left-width:3px}.pp-row--high{border-left-color:var(--danger)}.pp-row--medium{border-left-color:#eab308}.pp-row--low{border-left-color:#22c55e}.pp-row--purchased{opacity:.95;background:#fafbfc}.pp-row--clickable{cursor:pointer;transition:border-color .15s ease,background .15s ease}.pp-row--clickable:hover{border-color:color-mix(in srgb,var(--primary) 35%,var(--border));background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.pp-row--menu-open{z-index:10;overflow:visible}.pp-row--clickable{cursor:pointer;transition:background .15s ease}.pp-row--clickable:hover{background:#f8fafc}.pp-row--clickable:focus-visible{outline:2px solid var(--primary);outline-offset:-2px}.pp-row__body{flex:1;min-width:0}.pp-row__top{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:2px}.pp-row__main{flex:1;min-width:0}.pp-row__title{margin:0;font-size:14px;font-weight:700;line-height:1.25;min-width:0}.pp-row--compact .pp-row__title{font-size:14px}.pp-row__desc{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.pp-row__qty{flex-shrink:0;font-size:12px;color:var(--primary);font-weight:700;white-space:nowrap}.pp-row__note{margin:0 0 4px;font-size:12px;color:var(--muted);line-height:1.35;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}.pp-row__meta{margin:4px 0 0;font-size:9px;color:var(--muted-2);min-width:0}.pp-row__comments{display:inline-flex;align-items:center;gap:3px;white-space:nowrap}.pp-row__comments i{font-size:9px}.pp-row__actions-col{display:flex;flex-direction:column;align-items:center;gap:6px;flex-shrink:0}.pp-row__purchase-note{margin:0 0 6px;font-size:13px;color:var(--success);line-height:1.4}.pp-row__purchase-note i{margin-right:4px;font-size:12px}.pp-row__confirm{flex-shrink:0;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--success);border-radius:0;background:var(--success-soft);color:var(--success);font-size:12px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-row__confirm:hover{background:var(--success);color:#fff}.pp-row__confirm:disabled{opacity:.6;cursor:not-allowed}.pp-row__menu-wrap{position:relative;flex-shrink:0;align-self:center}.pp-icon-btn--menu{color:var(--muted-2)}.pp-row-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:148px;background:var(--surface);border:1px solid var(--border);border-radius:0;box-shadow:0 8px 24px #0f172a1f;padding:4px;z-index:20}.pp-row-menu__item{width:100%;border:none;background:transparent;border-radius:0;padding:8px 10px;display:inline-flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--text);cursor:pointer;text-align:left;font-family:inherit}.pp-row-menu__item:not(:last-child){border-bottom:1px solid var(--border)}.pp-row-menu__item:hover{background:var(--primary-soft);color:var(--primary)}.pp-row-menu__item--danger{color:var(--danger)}.pp-row-menu__item--danger:hover{background:var(--danger-soft);color:var(--danger)}.pp-row-menu__badge{margin-left:auto;font-size:11px;font-weight:700;padding:2px 7px;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-empty{margin:12px 0;text-align:center;color:var(--muted-2);font-size:14px}.pp-empty--group{margin:8px 0 0;font-size:13px}.pp-add-wrap{flex-shrink:0;padding:12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px));background:var(--surface);border-top:1px solid var(--border)}.pp-add{width:100%;border:none;border-radius:0;padding:11px 16px;background:var(--primary);color:#fff;font-size:15px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer}.pp-add:disabled{opacity:.6;cursor:not-allowed}.pp-overlay{position:fixed;inset:0;z-index:420;background:#0f172a80;display:flex;align-items:center;justify-content:center;padding:16px;padding-top:calc(16px + env(safe-area-inset-top,0px));padding-bottom:calc(16px + env(safe-area-inset-bottom,0px))}.pp-overlay--detail{background:#0f172a61;padding:12px}.pp-confirm{width:100%;max-width:360px;background:var(--surface);border-radius:0;padding:18px 16px}.pp-confirm__title{margin:0 0 8px;font-size:16px}.pp-confirm__body{margin:0 0 16px;color:var(--muted);font-size:14px}.pp-confirm__foot{display:flex;justify-content:flex-end;gap:8px}.pp-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer}.pp-btn--ghost{background:transparent;color:var(--muted)}.pp-btn--danger{background:var(--danger);color:#fff}.pp-btn--primary{background:var(--primary);color:#fff}.pp-btn--success{background:var(--success-soft);border:1px solid var(--success);color:var(--success)}.pp-btn--block{width:100%}.pp-btn--danger-soft{background:color-mix(in srgb,var(--danger) 10%,var(--surface));border:1px solid color-mix(in srgb,var(--danger) 35%,var(--border));color:var(--danger)}.pp-icon-btn--soft{width:34px;height:34px;border-radius:10px;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail{width:100%;max-width:420px;max-height:min(90vh,720px);background:var(--surface);border:1px solid var(--border);border-radius:16px;display:flex;flex-direction:column;overflow:hidden}.pp-detail__hero{padding:16px 16px 12px;border-bottom:1px solid color-mix(in srgb,var(--border) 70%,transparent)}.pp-detail__hero-top{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}.pp-detail__priority{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;background:var(--surface-2, #f1f5f9);color:var(--muted)}.pp-detail__priority--high{background:color-mix(in srgb,var(--danger) 12%,white);color:var(--danger)}.pp-detail__priority--medium{background:color-mix(in srgb,#d97706 12%,white);color:#d97706}.pp-detail__priority--low{background:color-mix(in srgb,var(--success) 12%,white);color:var(--success)}.pp-detail__priority .fa-solid{font-size:10px}.pp-detail__name{margin:0 0 8px;font-size:20px;font-weight:800;line-height:1.25;letter-spacing:-.02em;color:var(--text);overflow-wrap:anywhere}.pp-detail__meta{margin:0;display:flex;flex-wrap:wrap;gap:12px;font-size:12px;font-weight:600;color:var(--muted)}.pp-detail__meta span{display:inline-flex;align-items:center;gap:6px}.pp-detail__meta .fa-solid{font-size:11px;opacity:.75}.pp-detail__body{flex:1;overflow:auto;padding:14px 16px 16px;display:flex;flex-direction:column;gap:10px}.pp-detail__foot{display:flex;flex-direction:column;gap:8px;padding:12px 16px 16px;border-top:1px solid color-mix(in srgb,var(--border) 70%,transparent);background:color-mix(in srgb,var(--surface-2, #f8fafc) 50%,var(--surface))}.pp-detail__foot-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.pp-detail__foot-row .pp-btn{width:100%;padding:9px 8px;font-size:13px}.pp-detail-card{padding:12px 14px;border-radius:14px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent)}.pp-detail-card__title{margin:0 0 8px;font-size:12px;font-weight:700;color:var(--muted)}.pp-detail-card__text{margin:0;font-size:13px;line-height:1.5;color:var(--text);overflow-wrap:anywhere}.pp-detail-kv{margin:0}.pp-detail-kv__row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:5px 0}.pp-detail-kv__row:not(:last-child){border-bottom:1px solid color-mix(in srgb,var(--border) 55%,transparent)}.pp-detail-kv__row dt{margin:0;font-size:12px;font-weight:500;color:var(--muted)}.pp-detail-kv__row dd{margin:0;font-size:13px;font-weight:700;color:var(--text);text-align:right;overflow-wrap:anywhere}.pp-detail-kv__value--overdue{color:var(--danger)}.pp-comments{margin-top:4px}.pp-comments__head{display:flex;align-items:center;gap:8px;margin-bottom:10px}.pp-comments__title{margin:0;font-size:13px;font-weight:700;color:var(--text)}.pp-comments__count{font-size:11px;font-weight:700;min-width:22px;height:22px;padding:0 7px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;background:var(--primary-soft);color:var(--primary)}.pp-comments__list{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:8px}.pp-comments__empty{font-size:12px;line-height:1.45;color:var(--muted-2);padding:10px 12px;border-radius:12px;background:var(--surface-2, #f8fafc);border:1px dashed color-mix(in srgb,var(--border) 90%,transparent)}.pp-comments__composer{display:flex;align-items:flex-end;gap:8px;margin-top:12px}.pp-comments__input{flex:1;min-width:0;resize:vertical;min-height:44px;padding:10px 12px;border:1px solid color-mix(in srgb,var(--border) 90%,transparent);border-radius:12px;background:var(--surface);color:var(--text);font-size:13px;font-family:inherit;outline:none;line-height:1.4}.pp-comments__input:focus{border-color:var(--primary)}.pp-comments__send{flex-shrink:0;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:12px;background:var(--primary);color:#fff;font-size:14px;cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent}.pp-comments__send:hover:not(:disabled){filter:brightness(1.05)}.pp-comments__send:disabled{opacity:.5;cursor:not-allowed}.pp-comment{border-radius:12px;background:var(--surface-2, #f8fafc);border:1px solid color-mix(in srgb,var(--border) 80%,transparent);padding:10px 12px}.pp-comment__head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-bottom:6px}.pp-comment__author{font-size:12px;font-weight:700;color:var(--text)}.pp-comment__date{font-size:10px;font-weight:600;color:var(--muted-2);font-variant-numeric:tabular-nums}.pp-comment__text{margin:0;font-size:13px;color:var(--muted);line-height:1.45;overflow-wrap:anywhere;white-space:pre-wrap}.pp-comment__link{color:var(--primary);text-decoration:underline;text-underline-offset:2px;word-break:break-all}.pp-comment__link:hover{filter:brightness(.92)}.pp-purchase-entry{border:1px solid var(--border);background:#fafbfc;padding:12px;margin-bottom:10px}.pp-purchase-entry:last-child{margin-bottom:0}.pp-purchase-entry__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 10px;margin-bottom:8px}.pp-purchase-entry__index{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.03em;color:var(--primary)}.pp-purchase-entry__date{font-size:13px;font-weight:700;color:var(--text)}.pp-purchase-entry__qty{margin-left:auto;font-size:12px;font-weight:700;color:var(--muted)}.pp-purchase-entry__note{margin:0 0 10px;font-size:12px;line-height:1.4;color:var(--muted);overflow-wrap:anywhere}.pp-comments--nested{margin-top:0;padding-top:0;border-top:none}.pp-history-sheet{width:min(100%,520px);max-height:min(92vh,720px);margin:0 auto;background:var(--surface);border-radius:0;display:flex;flex-direction:column;overflow:hidden}.pp-history-sheet__head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:16px;border-bottom:1px solid var(--border)}.pp-history-sheet__title{margin:0;font-size:17px;font-weight:700}.pp-history-sheet__sub{margin:4px 0 0;font-size:13px;color:var(--muted)}.pp-history-sheet__body{flex:1;overflow:auto;padding:12px 16px}.pp-history-sheet__foot{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--border)}.pp-history-table{width:100%;border-collapse:collapse;font-size:13px}.pp-history-table th,.pp-history-table td{padding:10px 8px;text-align:left;border-bottom:1px solid var(--border);vertical-align:top}.pp-history-table th{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:var(--muted-2);background:#f8fafc;position:sticky;top:0}.pp-history-table tbody tr:last-child td{border-bottom:none}.pp-history-table td:first-child{width:36px;color:var(--muted-2);font-weight:700}.pp-history-table td:nth-child(3){white-space:nowrap}\n"] }]
23461
23751
  }], ctorParameters: () => [], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], back: [{ type: i0.Output, args: ["back"] }], pendingCountChange: [{ type: i0.Output, args: ["pendingCountChange"] }], onDocumentClick: [{
23462
23752
  type: HostListener,
23463
23753
  args: ['document:click']