@almatar/smart-booking-dashboard 0.0.2-alpha.8 → 0.0.2-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/components/booking-input/booking-input.component.mjs +2 -1
- package/esm2022/lib/components/train-details/train-details.component.mjs +89 -0
- package/esm2022/lib/config/dashboard.config.mjs +1 -1
- package/esm2022/lib/models/booking.types.mjs +1 -1
- package/esm2022/lib/models/train.types.mjs +2 -0
- package/esm2022/lib/pages/smart-booking-dashboard/smart-booking-dashboard.component.mjs +29 -7
- package/esm2022/lib/services/booking.service.mjs +8 -1
- package/fesm2022/almatar-smart-booking-dashboard.mjs +121 -6
- package/fesm2022/almatar-smart-booking-dashboard.mjs.map +1 -1
- package/lib/components/action-dialog/action-dialog.component.d.ts +1 -1
- package/lib/components/booking-input/booking-input.component.d.ts +3 -3
- package/lib/components/cancel-dialog/cancel-dialog.component.d.ts +2 -2
- package/lib/components/hotel-cancel/components/hotel-content/hotel-content.component.d.ts +1 -1
- package/lib/components/reissue-dialog/steps/reissue-notice/reissue-notice.component.d.ts +1 -1
- package/lib/components/result-panel/result-panel.component.d.ts +1 -1
- package/lib/components/train-details/train-details.component.d.ts +28 -0
- package/lib/config/dashboard.config.d.ts +5 -0
- package/lib/models/booking.types.d.ts +2 -0
- package/lib/models/train.types.d.ts +61 -0
- package/lib/pages/smart-booking-dashboard/smart-booking-dashboard.component.d.ts +5 -3
- package/lib/services/booking.service.d.ts +3 -0
- package/package.json +1 -1
|
@@ -125,6 +125,13 @@ class SbdBookingService {
|
|
|
125
125
|
})
|
|
126
126
|
.pipe(map(data => ({ data, elapsedMs: performance.now() - start })));
|
|
127
127
|
}
|
|
128
|
+
/** Train booking details: bilingual sections + summary, ready to show (same API Helix uses). */
|
|
129
|
+
getTrainDetails(id) {
|
|
130
|
+
const base = (this.config.trainApiBase ?? this.base).replace(/\/+$/, '');
|
|
131
|
+
return this.http.get(`${base}/crm/fcr/booking/${encodeURIComponent(id)}/details`, {
|
|
132
|
+
params: { product: 'trains' },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
128
135
|
resendTicket(id) {
|
|
129
136
|
return this.http.post(`${this.base}/v1/fcr/booking/${id}/resend`, {});
|
|
130
137
|
}
|
|
@@ -1264,6 +1271,7 @@ class SbdBookingInputComponent {
|
|
|
1264
1271
|
this.productOptions = [
|
|
1265
1272
|
{ label: '✈ Flight', value: 'flight' },
|
|
1266
1273
|
{ label: '🏨 Hotel', value: 'hotel' },
|
|
1274
|
+
{ label: '🚆 Train', value: 'train' },
|
|
1267
1275
|
];
|
|
1268
1276
|
this.demoIds = [
|
|
1269
1277
|
{ label: 'Demo Flight: ALM-20481', value: 'ALM-20481' },
|
|
@@ -5844,6 +5852,92 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
5844
5852
|
args: [{ selector: 'app-sbd-flight-itinerary-card', standalone: true, imports: [TagModule, UpperCasePipe, LowerCasePipe], template: "<div class=\"fic\">\n @if (product() === 'flight') {\n\n <!-- \u2500\u2500 Verified pill \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (verified()) {\n <div class=\"fic__verified-bar\">\n <span class=\"fic__verified-pill\">\n <i class=\"pi pi-verified\"></i> Verified with Airlines\n </span>\n </div>\n }\n\n <!-- \u2500\u2500 Legs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (leg of legs(); track leg.direction + $index; let li = $index) {\n <div class=\"fic__leg\" [class.fic__leg--return]=\"leg.direction === 'Return'\">\n\n <!-- Leg header -->\n <div class=\"fic__leg-header\">\n <div class=\"fic__leg-direction\">\n <i class=\"pi\" [class.pi-arrow-right]=\"leg.direction !== 'Return'\" [class.pi-arrow-left]=\"leg.direction === 'Return'\"></i>\n <span>{{ leg.direction }}</span>\n </div>\n <div class=\"fic__leg-chips\">\n @if (leg.cabin) {\n <span class=\"fic__chip fic__chip--cabin\">{{ leg.cabin }}</span>\n }\n @if (leg.fareBrand) {\n <span class=\"fic__chip fic__chip--fare\">{{ leg.fareBrand }}</span>\n }\n </div>\n </div>\n\n <!-- Airline row -->\n @if (leg.airlineName) {\n <div class=\"fic__airline\">\n <i class=\"pi pi-send\"></i>\n <span>{{ leg.airlineName }}</span>\n </div>\n }\n\n <!-- Route timeline -->\n <div class=\"fic__route\">\n\n <!-- Origin -->\n <div class=\"fic__airport\">\n <div class=\"fic__airport-code\">{{ leg.from }}</div>\n <div class=\"fic__airport-time\">{{ fmtTime(leg.depTime) }}</div>\n <div class=\"fic__airport-date\">{{ fmtShortDate(leg.depTime) }}</div>\n </div>\n\n <!-- Segments + stops -->\n <div class=\"fic__timeline\">\n\n <!-- Top row: all labels -->\n <div class=\"fic__tl-top\">\n @for (seg of leg.durationSegments; track seg.DepartureAirportCode; let si = $index) {\n <div class=\"fic__tl-seg\">\n <span class=\"fic__tl-flt\">{{ leg.segments[si]?.AirlineCode }}{{ leg.segments[si]?.FlightNumber }}</span>\n <span class=\"fic__tl-dur\">{{ fmtMins(seg.SegmentElapsedTimeMinutes) }}</span>\n </div>\n @if (si < leg.stops.length) {\n <div class=\"fic__tl-stop\">\n <span class=\"fic__stop-head\">\n <span class=\"fic__stop-dot\"></span>\n <span class=\"fic__stop-code\">{{ leg.stops[si].StopAirport }}</span>\n </span>\n <span class=\"fic__stop-dur\">{{ fmtMins(leg.stops[si].LayoverDuration) }}</span>\n </div>\n }\n }\n </div>\n\n <!-- Bottom row: single continuous line + one arrow -->\n <div class=\"fic__tl-track\">\n <div class=\"fic__tl-line\"></div>\n <i class=\"pi pi-send fic__tl-plane\"></i>\n </div>\n\n </div>\n\n <!-- Destination -->\n <div class=\"fic__airport fic__airport--right\">\n <div class=\"fic__airport-code\">{{ leg.to }}</div>\n <div class=\"fic__airport-time\">{{ fmtTime(leg.arrTime) }}</div>\n <div class=\"fic__airport-date\">{{ fmtShortDate(leg.arrTime) }}</div>\n </div>\n\n </div>\n </div>\n }\n\n <!-- \u2500\u2500 Passengers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (passengers().length > 0) {\n <div class=\"fic__section\">\n <div class=\"fic__section-label\">\n <i class=\"pi pi-users\"></i> Passengers\n <span class=\"fic__section-count\">{{ passengers().length }}</span>\n </div>\n <div class=\"fic__pax-list\">\n @for (p of passengers(); track p.passengerId) {\n <div class=\"fic__pax\">\n <div class=\"fic__pax-top\">\n <span class=\"fic__pax-badge fic__pax-badge--{{ p.type | lowercase }}\">{{ typeLabel[p.type] ?? p.type }}</span>\n <span class=\"fic__pax-name\">{{ p.name?.prefix }} {{ p.name?.givenName }} {{ p.name?.surName }}</span>\n </div>\n <div class=\"fic__pax-bottom\">\n @if (p.identity?.number) {\n <span class=\"fic__pax-doc\"><i class=\"pi pi-id-card\"></i> {{ p.identity?.number }}</span>\n }\n @if (p.email) {\n <span class=\"fic__pax-email\">{{ p.email }}</span>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2500\u2500 Payment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (payment()) {\n <div class=\"fic__payment\">\n <div class=\"fic__payment-main\">\n <i class=\"pi pi-credit-card fic__payment-icon\"></i>\n <span class=\"fic__payment-amount\">{{ payment()?.amount }} {{ payment()?.currency }}</span>\n <span class=\"fic__payment-status\" [class.fic__payment-status--ok]=\"payment()?.status?.key === 'SUCCESS'\">\n <i class=\"pi\" [class.pi-check-circle]=\"payment()?.status?.key === 'SUCCESS'\" [class.pi-clock]=\"payment()?.status?.key !== 'SUCCESS'\"></i>\n {{ payment()?.status?.name }}\n </span>\n </div>\n @if (payment()?.method || payment()?.date) {\n <div class=\"fic__payment-sub\">\n @if (payment()?.method) { <span>{{ payment()?.method }}</span> }\n @if (payment()?.method && payment()?.date) { <span class=\"fic__dot\">\u00B7</span> }\n @if (payment()?.date) { <span>{{ payment()?.date }}</span> }\n </div>\n }\n </div>\n }\n\n\n }\n</div>\n", styles: [".fic{background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden;font-family:inherit}.fic__leg{padding:16px 16px 18px;border-bottom:1px solid #e2e8f0}.fic__leg--return{background:#fafbff}.fic__leg-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.fic__leg-direction{display:flex;align-items:center;gap:6px;font-size:11px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:#1e2e4a}.fic__leg-direction .pi{font-size:10px;color:#475569}.fic__leg-chips{display:flex;gap:5px}.fic__chip{font-size:10.5px;font-weight:600;padding:2px 8px;border-radius:12px;letter-spacing:.02em}.fic__chip--cabin{background:#eff6ff;color:#1d4ed8}.fic__chip--fare{background:#fef9c3;color:#854d0e}.fic__airline{display:flex;align-items:center;gap:5px;font-size:12px;color:#64748b;margin-bottom:14px}.fic__airline .pi{font-size:11px;color:#94a3b8;transform:rotate(45deg)}.fic__route{display:flex;align-items:flex-start;gap:0}.fic__airport{display:flex;flex-direction:column;align-items:flex-start;flex-shrink:0}.fic__airport--right{align-items:flex-end}.fic__airport-code{font-size:26px;font-weight:800;color:#1e2e4a;line-height:1}.fic__airport-time{font-size:13px;font-weight:600;color:#334155;margin-top:3px}.fic__airport-date{font-size:10.5px;color:#94a3b8;margin-top:1px}.fic__timeline{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;margin:0 4px}.fic__tl-top{display:flex;align-items:flex-end;gap:0}.fic__tl-seg{flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;gap:1px}.fic__tl-flt{font-size:10px;font-weight:700;color:#1e2e4a;background:#f1f5f9;padding:1px 5px;border-radius:4px;white-space:nowrap}.fic__tl-dur{font-size:9.5px;color:#94a3b8;white-space:nowrap}.fic__tl-stop{flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:1px;padding:0 6px}.fic__stop-head{display:inline-flex;align-items:center;gap:4px}.fic__tl-track{display:flex;align-items:center;height:22px}.fic__tl-line{flex:1;height:2px;background:#cbd5e1}.fic__tl-plane{font-size:13px;color:#475569;transform:rotate(45deg);flex-shrink:0;margin-left:-2px}.fic__stop-dot{width:7px;height:7px;border-radius:50%;background:#94a3b8;flex-shrink:0}.fic__stop-code{font-size:11px;font-weight:700;color:#334155}.fic__stop-dur{font-size:10.5px;font-weight:600;color:#f59e0b;white-space:nowrap}.fic__section{padding:14px 16px;border-bottom:1px solid #e2e8f0}.fic__section-label{display:flex;align-items:center;gap:5px;font-size:10.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:#64748b;margin-bottom:10px}.fic__section-label .pi{font-size:11px}.fic__section-count{margin-left:auto;background:#f1f5f9;color:#475569;font-size:10px;font-weight:700;padding:1px 6px;border-radius:10px}.fic__pax-list{display:flex;flex-direction:column;gap:8px}.fic__pax{background:#f8fafc;border-radius:8px;padding:8px 10px;display:flex;flex-direction:column;gap:5px}.fic__pax-top{display:flex;align-items:center;gap:7px}.fic__pax-badge{font-size:9.5px;font-weight:700;letter-spacing:.04em;padding:2px 7px;border-radius:10px;flex-shrink:0;text-transform:uppercase}.fic__pax-badge--adt{background:#dbeafe;color:#1d4ed8}.fic__pax-badge--cnn{background:#dcfce7;color:#15803d}.fic__pax-badge--inf{background:#fce7f3;color:#9d174d}.fic__pax-name{font-size:12.5px;font-weight:600;color:#1e2e4a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.fic__pax-bottom{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.fic__pax-doc{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-family:monospace;color:#475569;background:#f1f5f9;padding:1px 6px;border-radius:4px}.fic__pax-doc .pi{font-size:10px}.fic__pax-email{font-size:11px;color:#94a3b8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:180px}.fic__payment{padding:12px 16px;background:#f8fafc;display:flex;flex-direction:column;gap:4px}.fic__payment-main{display:flex;align-items:center;gap:8px}.fic__payment-icon{font-size:14px;color:#64748b}.fic__payment-amount{font-size:14px;font-weight:700;color:#1e2e4a}.fic__payment-status{display:flex;align-items:center;gap:4px;font-size:12px;font-weight:600;color:#64748b;margin-left:auto}.fic__payment-status .pi{font-size:12px}.fic__payment-status--ok{color:#16a34a}.fic__payment-sub{display:flex;align-items:center;gap:6px;font-size:11.5px;color:#94a3b8;padding-left:22px}.fic__dot{color:#cbd5e1}.fic__verified-bar{display:flex;align-items:center;padding:8px 12px;background:#f0fdf4;border-bottom:1px solid #bbf7d0}.fic__verified-pill{display:inline-flex;align-items:center;gap:5px;padding:3px 10px 3px 7px;background:#16a34a;color:#fff;font-size:11px;font-weight:700;border-radius:20px;letter-spacing:.02em}.fic__verified-pill .pi{font-size:12px}\n"] }]
|
|
5845
5853
|
}] });
|
|
5846
5854
|
|
|
5855
|
+
/** Icon and subtitle of each section card. Keys may carry an index (journey_0, fare_1): the base key covers them all. */
|
|
5856
|
+
const SECTION_META = {
|
|
5857
|
+
journey: { icon: 'pi-arrow-right', sub: 'Operator · Stations · Times' },
|
|
5858
|
+
pnr: { icon: 'pi-tag', sub: 'PNR · Booking ref' },
|
|
5859
|
+
seat: { icon: 'pi-ticket', sub: 'Coach · Seat · Class' },
|
|
5860
|
+
fare: { icon: 'pi-list', sub: 'Fare rules · Conditions' },
|
|
5861
|
+
refund: { icon: 'pi-undo', sub: 'Refund · Penalty' },
|
|
5862
|
+
payment: { icon: 'pi-credit-card', sub: 'Amount · Taxes' },
|
|
5863
|
+
};
|
|
5864
|
+
/** Train booking details (read only): journey summary, passengers, payment and the bilingual sections to copy. */
|
|
5865
|
+
class SbdTrainDetailsComponent {
|
|
5866
|
+
constructor() {
|
|
5867
|
+
this.detail = input.required();
|
|
5868
|
+
this.bookingId = input.required();
|
|
5869
|
+
/** Same as Helix for a train booking: read only, only the non-mutating "coming soon" actions. */
|
|
5870
|
+
this.quickActions = [
|
|
5871
|
+
{ icon: 'pi-send', label: 'Send Ticket' },
|
|
5872
|
+
{ icon: 'pi-plus-circle', label: 'Add Service' },
|
|
5873
|
+
];
|
|
5874
|
+
this.picked = signal('');
|
|
5875
|
+
this.copied = signal(false);
|
|
5876
|
+
this.summary = computed(() => this.detail().summary);
|
|
5877
|
+
this.sections = computed(() => this.detail().sections ?? []);
|
|
5878
|
+
this.current = computed(() => this.sections().find((s) => s.key === this.picked()) ?? this.sections()[0]);
|
|
5879
|
+
}
|
|
5880
|
+
select(key) {
|
|
5881
|
+
this.picked.set(key);
|
|
5882
|
+
}
|
|
5883
|
+
meta(key) {
|
|
5884
|
+
return SECTION_META[key] ?? SECTION_META[key.replace(/_\d+$/, '')] ?? { icon: 'pi-info-circle', sub: '' };
|
|
5885
|
+
}
|
|
5886
|
+
isReturn(leg) {
|
|
5887
|
+
return /return/i.test(leg.direction || '');
|
|
5888
|
+
}
|
|
5889
|
+
format(section, lang) {
|
|
5890
|
+
const lines = [`*${lang === 'ar' ? section.titleAr : section.title}*`];
|
|
5891
|
+
for (const r of section.rows) {
|
|
5892
|
+
const label = lang === 'ar' ? r.labelAr : r.label;
|
|
5893
|
+
const value = (lang === 'ar' ? r.ar : r.en) || '';
|
|
5894
|
+
if (value.includes('\n'))
|
|
5895
|
+
lines.push(`${label}:`, ...value.split('\n').map((l) => ` ${l}`));
|
|
5896
|
+
else
|
|
5897
|
+
lines.push(`${label}: ${value}`);
|
|
5898
|
+
}
|
|
5899
|
+
return lines.join('\n');
|
|
5900
|
+
}
|
|
5901
|
+
copy(mode) {
|
|
5902
|
+
const s = this.current();
|
|
5903
|
+
if (!s)
|
|
5904
|
+
return;
|
|
5905
|
+
const text = mode === 'both' ? `${this.format(s, 'en')}\n\n${this.format(s, 'ar')}` : this.format(s, mode);
|
|
5906
|
+
const done = () => {
|
|
5907
|
+
this.copied.set(true);
|
|
5908
|
+
setTimeout(() => this.copied.set(false), 1600);
|
|
5909
|
+
};
|
|
5910
|
+
if (navigator.clipboard?.writeText) {
|
|
5911
|
+
navigator.clipboard.writeText(text).then(done, () => this.fallbackCopy(text, done));
|
|
5912
|
+
}
|
|
5913
|
+
else {
|
|
5914
|
+
this.fallbackCopy(text, done);
|
|
5915
|
+
}
|
|
5916
|
+
}
|
|
5917
|
+
fallbackCopy(text, done) {
|
|
5918
|
+
const ta = document.createElement('textarea');
|
|
5919
|
+
ta.value = text;
|
|
5920
|
+
ta.style.position = 'fixed';
|
|
5921
|
+
ta.style.opacity = '0';
|
|
5922
|
+
document.body.appendChild(ta);
|
|
5923
|
+
ta.select();
|
|
5924
|
+
try {
|
|
5925
|
+
document.execCommand('copy');
|
|
5926
|
+
done();
|
|
5927
|
+
}
|
|
5928
|
+
catch {
|
|
5929
|
+
/* ignore: the browser blocked the copy */
|
|
5930
|
+
}
|
|
5931
|
+
document.body.removeChild(ta);
|
|
5932
|
+
}
|
|
5933
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SbdTrainDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5934
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SbdTrainDetailsComponent, isStandalone: true, selector: "app-sbd-train-details", inputs: { detail: { classPropertyName: "detail", publicName: "detail", isSignal: true, isRequired: true, transformFunction: null }, bookingId: { classPropertyName: "bookingId", publicName: "bookingId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"std\">\n <div class=\"std__head\">\n <span class=\"std__ico\"><i class=\"pi pi-bookmark\"></i></span>\n <div>\n <h3 class=\"std__title\">Booking details</h3>\n <div class=\"std__sub\">\n <b>{{ bookingId() }}</b>\n @if (detail().headline) { <span>\u00B7 {{ detail().headline }}</span> }\n </div>\n </div>\n </div>\n\n @if (!detail().configured) {\n <div class=\"std__state std__state--warn\">Train booking details are not available on this server.</div>\n } @else if (!sections().length && !summary()) {\n <div class=\"std__state std__state--warn\">No details found for this train booking.</div>\n } @else {\n <div class=\"std__grid\">\n\n <!-- Left: journey, passengers, payment -->\n <aside class=\"std__rail\">\n @if (summary()?.verified) {\n <div class=\"std__verified\"><i class=\"pi pi-check-circle\"></i> Verified with the operator</div>\n }\n\n @for (t of summary()?.trainLegs ?? []; track $index) {\n <div class=\"std__leg\">\n <div class=\"std__leg-top\">\n <span class=\"std__dir\">\n <i class=\"pi\" [ngClass]=\"isReturn(t) ? 'pi-arrow-left' : 'pi-arrow-right'\"></i>\n {{ t.direction || (isReturn(t) ? 'Return' : 'Onward') }}\n </span>\n <span class=\"std__badges\">\n @if (t.fareClass) { <span class=\"std__badge std__badge--fare\">{{ t.fareClass }}</span> }\n @if (t.status) { <span class=\"std__badge\">{{ t.status }}</span> }\n </span>\n </div>\n @if (t.operator || t.trainNumber) {\n <div class=\"std__operator\">{{ t.operator }}@if (t.trainNumber) { <span> \u00B7 {{ t.trainNumber }}</span> }</div>\n }\n <div class=\"std__itin\">\n <div class=\"std__stop\">\n <div class=\"std__node\"></div>\n <div class=\"std__city\">{{ t.fromCity || t.fromStation }}</div>\n @if (t.fromCity && t.fromStation) { <div class=\"std__station\">{{ t.fromStation }}</div> }\n <div class=\"std__time\">{{ t.depTime }}</div>\n <div class=\"std__date\">{{ t.depDate }}</div>\n </div>\n <div class=\"std__seg\">\n <div class=\"std__bar\"><i class=\"pi pi-arrow-right\"></i></div>\n @if (t.duration) { <span class=\"std__dur\">{{ t.duration }}</span> }\n </div>\n <div class=\"std__stop std__stop--end\">\n <div class=\"std__node\"></div>\n <div class=\"std__city\">{{ t.toCity || t.toStation }}</div>\n @if (t.toCity && t.toStation) { <div class=\"std__station\">{{ t.toStation }}</div> }\n <div class=\"std__time\">{{ t.arrTime }}</div>\n <div class=\"std__date\">{{ t.arrDate }}</div>\n </div>\n </div>\n </div>\n }\n\n @if ((summary()?.passengers ?? []).length) {\n <div class=\"std__box\">\n <div class=\"std__box-h\"><i class=\"pi pi-users\"></i> Passengers <span class=\"std__n\">{{ summary()?.passengers?.length }}</span></div>\n @for (p of summary()?.passengers ?? []; track $index) {\n <div class=\"std__pax\">\n <span class=\"std__ptag\">{{ p.typeLabel }}</span>\n <div class=\"std__pinfo\">\n <div class=\"std__pname\">{{ p.name }}</div>\n <div class=\"std__pdoc\">\n {{ p.doc }}@if (p.email) { <span>{{ p.doc ? ' \u00B7 ' : '' }}{{ p.email }}</span> }\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n @if (summary()?.payment; as pay) {\n <div class=\"std__pay\">\n <div class=\"std__pay-l\">\n <i class=\"pi pi-credit-card\"></i>\n <div>\n <div class=\"std__amt\">{{ pay.amount }} {{ pay.currency }}</div>\n <div class=\"std__pmeta\">{{ pay.method }}@if (pay.date) { <span> \u00B7 {{ pay.date }}</span> }</div>\n </div>\n </div>\n @if (pay.status) { <span class=\"std__pok\"><i class=\"pi pi-check-circle\"></i> {{ pay.status }}</span> }\n </div>\n }\n </aside>\n\n <!-- Right: section cards and the selected section -->\n <section class=\"std__main\">\n <div class=\"std__qa\">\n <div class=\"std__qa-h\">Quick actions</div>\n <div class=\"std__qa-row\">\n @for (a of quickActions; track a.label) {\n <button type=\"button\" class=\"std__qa-btn\" disabled title=\"Coming soon\">\n <i class=\"pi\" [ngClass]=\"a.icon\"></i> {{ a.label }}\n </button>\n }\n </div>\n </div>\n\n <div class=\"std__main-h\">Booking sections</div>\n <div class=\"std__cards\">\n @for (s of sections(); track s.key) {\n <button type=\"button\" class=\"std__card\" [class.std__card--on]=\"current()?.key === s.key\" (click)=\"select(s.key)\">\n <span class=\"std__card-ico\"><i class=\"pi\" [ngClass]=\"meta(s.key).icon\"></i></span>\n <span class=\"std__card-t\">{{ s.title }}</span>\n <span class=\"std__card-s\">{{ meta(s.key).sub }}</span>\n </button>\n }\n </div>\n\n @if (current(); as cur) {\n <div class=\"std__panel\">\n <div class=\"std__panel-h\">\n <i class=\"pi\" [ngClass]=\"meta(cur.key).icon\"></i> {{ cur.title }}\n <small dir=\"rtl\">{{ cur.titleAr }}</small>\n </div>\n <div class=\"std__rows\">\n @for (r of cur.rows; track $index) {\n <div class=\"std__row\">\n <div class=\"std__label\">{{ r.label }}<small dir=\"rtl\">{{ r.labelAr }}</small></div>\n <div class=\"std__val\">\n <div class=\"std__en\">{{ r.en }}</div>\n @if (r.ar && r.ar !== r.en) { <div class=\"std__ar\" dir=\"rtl\">{{ r.ar }}</div> }\n </div>\n </div>\n }\n </div>\n <div class=\"std__copybar\">\n @if (copied()) {\n <span class=\"std__copied\"><i class=\"pi pi-check\"></i> Copied</span>\n } @else {\n <span class=\"std__hint\">Copy this section to paste in the chat</span>\n }\n <span class=\"std__spacer\"></span>\n <button type=\"button\" class=\"std__btn\" (click)=\"copy('en')\"><i class=\"pi pi-copy\"></i> EN</button>\n <button type=\"button\" class=\"std__btn\" (click)=\"copy('ar')\"><i class=\"pi pi-copy\"></i> AR</button>\n <button type=\"button\" class=\"std__btn std__btn--primary\" (click)=\"copy('both')\"><i class=\"pi pi-copy\"></i> Both</button>\n </div>\n </div>\n }\n </section>\n </div>\n }\n</div>\n", styles: [".std{background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:12px;overflow:hidden;font-family:inherit;color:var(--sbd-r-ink, #1e2e4a)}.std__head{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--sbd-r-line, #e2e8f0)}.std__ico{width:38px;height:38px;border-radius:10px;background:var(--std-accent-soft, #eff6ff);color:#007ad9;display:grid;place-items:center;flex:none}.std__title{margin:0;font-size:16px;font-weight:700}.std__sub{color:var(--sbd-r-muted, #64748b);font-size:12.5px;display:flex;gap:6px}.std__sub b{color:var(--sbd-r-ink, #1e2e4a)}.std__state{padding:40px;text-align:center;color:var(--sbd-r-muted, #64748b);font-size:14px}.std__state--warn{color:var(--sbd-r-warn-text, #b26a00)}.std__grid{display:grid;grid-template-columns:342px minmax(0,1fr)}.std__rail{border-right:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-soft, #f8fafc);padding:16px}.std__main{padding:18px 20px;min-width:0}.std__verified{display:inline-flex;align-items:center;gap:8px;margin-bottom:14px;padding:8px 14px;border-radius:10px;background:var(--sbd-r-success-bg, #e7f7ec);color:var(--sbd-r-success-text, #1a7f37);border:1px solid var(--sbd-r-success-line, #b7e6c4);font-weight:700;font-size:12.5px}.std__leg{background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;padding:14px;margin-bottom:12px}.std__leg-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.std__dir{font-size:11.5px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;display:inline-flex;align-items:center;gap:6px}.std__badges{display:flex;gap:5px}.std__badge{font-size:10.5px;font-weight:700;padding:3px 8px;border-radius:999px;background:var(--std-accent-soft, #eff6ff);color:#007ad9}.std__badge--fare{background:var(--sbd-r-warn-bg, #fff3d6);color:var(--sbd-r-warn-text, #a56a00)}.std__operator{font-size:12.5px;color:var(--sbd-r-muted, #64748b);margin-bottom:12px}.std__itin{display:flex;align-items:flex-start}.std__stop{display:flex;flex-direction:column;flex:none;align-items:flex-start}.std__stop--end{align-items:flex-end;text-align:right}.std__node{width:10px;height:10px;border-radius:50%;background:#007ad9;margin-bottom:8px}.std__city{font-size:15px;font-weight:800;line-height:1.15;max-width:130px}.std__station{font-size:10.5px;color:var(--sbd-r-faint, #94a3b8);margin-top:2px;line-height:1.2;max-width:130px}.std__time{font-size:13.5px;font-weight:700;margin-top:4px}.std__date{font-size:11px;color:var(--sbd-r-faint, #94a3b8);margin-top:1px}.std__seg{flex:1;min-width:26px;display:flex;flex-direction:column;align-items:center}.std__bar{position:relative;width:100%;height:2px;background:var(--sbd-r-line, #e2e8f0);margin-top:4px}.std__bar .pi{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:9px;color:#007ad9;background:var(--sbd-r-surface, #fff);padding:0 3px}.std__dur{font-size:9.5px;font-weight:700;background:var(--sbd-r-soft, #f8fafc);border:1px solid var(--sbd-r-line, #e2e8f0);color:var(--sbd-r-muted, #64748b);padding:1px 6px;border-radius:6px;margin-top:7px;white-space:nowrap}.std__box{background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;padding:12px 14px;margin-bottom:12px}.std__box-h{font-size:11.5px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;color:var(--sbd-r-muted, #64748b);display:flex;align-items:center;gap:7px;margin-bottom:10px}.std__n{margin-left:auto;background:var(--sbd-r-sunk, #f1f5f9);color:var(--sbd-r-muted, #64748b);border-radius:999px;padding:1px 8px;font-size:11px}.std__pax{display:flex;align-items:center;gap:10px;padding:7px 0;border-top:1px dashed var(--sbd-r-line, #e2e8f0)}.std__pax:first-of-type{border-top:none}.std__ptag{flex:none;font-size:10px;font-weight:800;text-transform:uppercase;padding:3px 8px;border-radius:6px;background:var(--std-accent-soft, #eff6ff);color:#007ad9}.std__pinfo{min-width:0}.std__pname{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.std__pdoc{font-size:11px;color:var(--sbd-r-faint, #94a3b8);word-break:break-all}.std__pay{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;padding:12px 14px}.std__pay-l{display:flex;gap:10px;align-items:center}.std__pay-l>.pi{color:#007ad9;font-size:15px}.std__amt{font-size:15px;font-weight:800}.std__pmeta{font-size:11px;color:var(--sbd-r-faint, #94a3b8)}.std__pok{flex:none;font-size:11.5px;font-weight:700;color:var(--sbd-r-success-text, #1a7f37);display:inline-flex;align-items:center;gap:5px}.std__main-h{font-size:15px;font-weight:700;margin-bottom:12px}.std__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-bottom:16px}.std__card{text-align:left;border:1.5px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-surface, #fff);border-radius:13px;padding:12px;cursor:pointer;display:flex;flex-direction:column;gap:3px;font-family:inherit;transition:border-color .12s,box-shadow .12s}.std__card:hover{border-color:var(--std-hover-line, #bfdbfe)}.std__card--on{border-color:#007ad9;box-shadow:0 0 0 3px var(--std-accent-soft, #eff6ff)}.std__card-ico{width:30px;height:30px;border-radius:8px;background:var(--sbd-r-sunk, #f1f5f9);color:var(--sbd-r-muted, #64748b);display:grid;place-items:center;margin-bottom:5px}.std__card--on .std__card-ico{background:var(--std-accent-soft, #eff6ff);color:#007ad9}.std__card-t{font-size:13px;font-weight:700}.std__card--on .std__card-t{color:#007ad9}.std__card-s{font-size:11px;color:var(--sbd-r-faint, #94a3b8)}.std__panel{border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;overflow:hidden}.std__panel-h{padding:12px 16px;border-bottom:1px solid var(--sbd-r-line, #e2e8f0);font-size:13.5px;font-weight:700;background:var(--sbd-r-soft, #f8fafc);display:flex;align-items:center;gap:8px}.std__panel-h .pi{color:#007ad9}.std__panel-h small{font-weight:500;color:var(--sbd-r-faint, #94a3b8)}.std__rows{padding:4px 16px;max-height:40vh;overflow-y:auto}.std__row{display:grid;grid-template-columns:160px 1fr;gap:12px;padding:9px 0;border-bottom:1px dashed var(--sbd-r-line, #e2e8f0)}.std__row:last-child{border-bottom:none}.std__label{font-size:12.5px;font-weight:600;color:var(--sbd-r-muted, #64748b);display:flex;flex-direction:column;gap:2px}.std__label small{font-weight:500;color:var(--sbd-r-faint, #94a3b8)}.std__val{font-size:13.5px;min-width:0}.std__en{white-space:pre-line;word-break:break-word}.std__ar{white-space:pre-line;word-break:break-word;color:var(--sbd-r-muted, #64748b);margin-top:3px}.std__copybar{display:flex;align-items:center;gap:10px;padding:12px 16px;border-top:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-soft, #f8fafc)}.std__hint{font-size:12px;color:var(--sbd-r-faint, #94a3b8)}.std__copied{color:var(--sbd-r-success-text, #1a7f37);font-size:12.5px;font-weight:700}.std__spacer{flex:1}.std__btn{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-surface, #fff);color:var(--sbd-r-ink, #1e2e4a);border-radius:9px;padding:8px 13px;font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit}.std__btn--primary{background:#007ad9;border-color:#007ad9;color:#fff}@media (max-width: 820px){.std__grid{grid-template-columns:minmax(0,1fr)}.std__rail{border-right:none;border-bottom:1px solid var(--sbd-r-line, #e2e8f0)}}.std__qa{margin-bottom:18px}.std__qa-h{font-size:11.5px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:var(--sbd-r-muted, #64748b);margin-bottom:10px}.std__qa-row{display:flex;flex-wrap:wrap;gap:8px}.std__qa-btn{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-surface, #fff);color:var(--sbd-r-muted, #64748b);border-radius:9px;padding:8px 13px;font-size:12.5px;font-weight:600;font-family:inherit;cursor:not-allowed;opacity:.6}:host-context(.dark) .std,:host-context([data-theme=dark]) .std{--sbd-r-bg: #18181b;--sbd-r-surface: #1f1f23;--sbd-r-soft: #27272a;--sbd-r-sunk: #2f2f34;--sbd-r-ink: #f4f4f5;--sbd-r-muted: #a1a1aa;--sbd-r-faint: #8b8b94;--sbd-r-line: #3f3f46;--sbd-r-line-strong: #52525b;--sbd-r-label: #d4d4d8;--sbd-r-input-text: #e4e4e7;--sbd-r-info-bg: #172554;--sbd-r-info-line: #1e3a8a;--sbd-r-info-text: #93c5fd;--sbd-r-warn-bg: #3a2a0e;--sbd-r-warn-line: #6b4a16;--sbd-r-warn-text: #fcd34d;--sbd-r-warn-icon: #f59e0b;--sbd-r-error-bg: #3a1517;--sbd-r-error-line: #7f1d1d;--sbd-r-error-text: #fca5a5;--sbd-r-success-bg: #0f2a1a;--sbd-r-success-line: #166534;--sbd-r-success-text: #86efac;--sbd-r-state-bg: #3a2410;--sbd-r-state-line: #7c3a10;--sbd-r-state-warn-bg: #3a1517;--sbd-r-state-warn-line: #7f1d1d;--sbd-r-green: #4ade80;--sbd-r-expiry-soon: #fbbf24;--sbd-r-accent-soft: color-mix(in srgb, var(--sbd-accent, #6d4aff) 28%, transparent);--std-accent-soft: #1e2a52;--std-hover-line: #3b5ea8}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
5935
|
+
}
|
|
5936
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SbdTrainDetailsComponent, decorators: [{
|
|
5937
|
+
type: Component,
|
|
5938
|
+
args: [{ selector: 'app-sbd-train-details', standalone: true, imports: [NgClass], template: "<div class=\"std\">\n <div class=\"std__head\">\n <span class=\"std__ico\"><i class=\"pi pi-bookmark\"></i></span>\n <div>\n <h3 class=\"std__title\">Booking details</h3>\n <div class=\"std__sub\">\n <b>{{ bookingId() }}</b>\n @if (detail().headline) { <span>\u00B7 {{ detail().headline }}</span> }\n </div>\n </div>\n </div>\n\n @if (!detail().configured) {\n <div class=\"std__state std__state--warn\">Train booking details are not available on this server.</div>\n } @else if (!sections().length && !summary()) {\n <div class=\"std__state std__state--warn\">No details found for this train booking.</div>\n } @else {\n <div class=\"std__grid\">\n\n <!-- Left: journey, passengers, payment -->\n <aside class=\"std__rail\">\n @if (summary()?.verified) {\n <div class=\"std__verified\"><i class=\"pi pi-check-circle\"></i> Verified with the operator</div>\n }\n\n @for (t of summary()?.trainLegs ?? []; track $index) {\n <div class=\"std__leg\">\n <div class=\"std__leg-top\">\n <span class=\"std__dir\">\n <i class=\"pi\" [ngClass]=\"isReturn(t) ? 'pi-arrow-left' : 'pi-arrow-right'\"></i>\n {{ t.direction || (isReturn(t) ? 'Return' : 'Onward') }}\n </span>\n <span class=\"std__badges\">\n @if (t.fareClass) { <span class=\"std__badge std__badge--fare\">{{ t.fareClass }}</span> }\n @if (t.status) { <span class=\"std__badge\">{{ t.status }}</span> }\n </span>\n </div>\n @if (t.operator || t.trainNumber) {\n <div class=\"std__operator\">{{ t.operator }}@if (t.trainNumber) { <span> \u00B7 {{ t.trainNumber }}</span> }</div>\n }\n <div class=\"std__itin\">\n <div class=\"std__stop\">\n <div class=\"std__node\"></div>\n <div class=\"std__city\">{{ t.fromCity || t.fromStation }}</div>\n @if (t.fromCity && t.fromStation) { <div class=\"std__station\">{{ t.fromStation }}</div> }\n <div class=\"std__time\">{{ t.depTime }}</div>\n <div class=\"std__date\">{{ t.depDate }}</div>\n </div>\n <div class=\"std__seg\">\n <div class=\"std__bar\"><i class=\"pi pi-arrow-right\"></i></div>\n @if (t.duration) { <span class=\"std__dur\">{{ t.duration }}</span> }\n </div>\n <div class=\"std__stop std__stop--end\">\n <div class=\"std__node\"></div>\n <div class=\"std__city\">{{ t.toCity || t.toStation }}</div>\n @if (t.toCity && t.toStation) { <div class=\"std__station\">{{ t.toStation }}</div> }\n <div class=\"std__time\">{{ t.arrTime }}</div>\n <div class=\"std__date\">{{ t.arrDate }}</div>\n </div>\n </div>\n </div>\n }\n\n @if ((summary()?.passengers ?? []).length) {\n <div class=\"std__box\">\n <div class=\"std__box-h\"><i class=\"pi pi-users\"></i> Passengers <span class=\"std__n\">{{ summary()?.passengers?.length }}</span></div>\n @for (p of summary()?.passengers ?? []; track $index) {\n <div class=\"std__pax\">\n <span class=\"std__ptag\">{{ p.typeLabel }}</span>\n <div class=\"std__pinfo\">\n <div class=\"std__pname\">{{ p.name }}</div>\n <div class=\"std__pdoc\">\n {{ p.doc }}@if (p.email) { <span>{{ p.doc ? ' \u00B7 ' : '' }}{{ p.email }}</span> }\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n @if (summary()?.payment; as pay) {\n <div class=\"std__pay\">\n <div class=\"std__pay-l\">\n <i class=\"pi pi-credit-card\"></i>\n <div>\n <div class=\"std__amt\">{{ pay.amount }} {{ pay.currency }}</div>\n <div class=\"std__pmeta\">{{ pay.method }}@if (pay.date) { <span> \u00B7 {{ pay.date }}</span> }</div>\n </div>\n </div>\n @if (pay.status) { <span class=\"std__pok\"><i class=\"pi pi-check-circle\"></i> {{ pay.status }}</span> }\n </div>\n }\n </aside>\n\n <!-- Right: section cards and the selected section -->\n <section class=\"std__main\">\n <div class=\"std__qa\">\n <div class=\"std__qa-h\">Quick actions</div>\n <div class=\"std__qa-row\">\n @for (a of quickActions; track a.label) {\n <button type=\"button\" class=\"std__qa-btn\" disabled title=\"Coming soon\">\n <i class=\"pi\" [ngClass]=\"a.icon\"></i> {{ a.label }}\n </button>\n }\n </div>\n </div>\n\n <div class=\"std__main-h\">Booking sections</div>\n <div class=\"std__cards\">\n @for (s of sections(); track s.key) {\n <button type=\"button\" class=\"std__card\" [class.std__card--on]=\"current()?.key === s.key\" (click)=\"select(s.key)\">\n <span class=\"std__card-ico\"><i class=\"pi\" [ngClass]=\"meta(s.key).icon\"></i></span>\n <span class=\"std__card-t\">{{ s.title }}</span>\n <span class=\"std__card-s\">{{ meta(s.key).sub }}</span>\n </button>\n }\n </div>\n\n @if (current(); as cur) {\n <div class=\"std__panel\">\n <div class=\"std__panel-h\">\n <i class=\"pi\" [ngClass]=\"meta(cur.key).icon\"></i> {{ cur.title }}\n <small dir=\"rtl\">{{ cur.titleAr }}</small>\n </div>\n <div class=\"std__rows\">\n @for (r of cur.rows; track $index) {\n <div class=\"std__row\">\n <div class=\"std__label\">{{ r.label }}<small dir=\"rtl\">{{ r.labelAr }}</small></div>\n <div class=\"std__val\">\n <div class=\"std__en\">{{ r.en }}</div>\n @if (r.ar && r.ar !== r.en) { <div class=\"std__ar\" dir=\"rtl\">{{ r.ar }}</div> }\n </div>\n </div>\n }\n </div>\n <div class=\"std__copybar\">\n @if (copied()) {\n <span class=\"std__copied\"><i class=\"pi pi-check\"></i> Copied</span>\n } @else {\n <span class=\"std__hint\">Copy this section to paste in the chat</span>\n }\n <span class=\"std__spacer\"></span>\n <button type=\"button\" class=\"std__btn\" (click)=\"copy('en')\"><i class=\"pi pi-copy\"></i> EN</button>\n <button type=\"button\" class=\"std__btn\" (click)=\"copy('ar')\"><i class=\"pi pi-copy\"></i> AR</button>\n <button type=\"button\" class=\"std__btn std__btn--primary\" (click)=\"copy('both')\"><i class=\"pi pi-copy\"></i> Both</button>\n </div>\n </div>\n }\n </section>\n </div>\n }\n</div>\n", styles: [".std{background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:12px;overflow:hidden;font-family:inherit;color:var(--sbd-r-ink, #1e2e4a)}.std__head{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--sbd-r-line, #e2e8f0)}.std__ico{width:38px;height:38px;border-radius:10px;background:var(--std-accent-soft, #eff6ff);color:#007ad9;display:grid;place-items:center;flex:none}.std__title{margin:0;font-size:16px;font-weight:700}.std__sub{color:var(--sbd-r-muted, #64748b);font-size:12.5px;display:flex;gap:6px}.std__sub b{color:var(--sbd-r-ink, #1e2e4a)}.std__state{padding:40px;text-align:center;color:var(--sbd-r-muted, #64748b);font-size:14px}.std__state--warn{color:var(--sbd-r-warn-text, #b26a00)}.std__grid{display:grid;grid-template-columns:342px minmax(0,1fr)}.std__rail{border-right:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-soft, #f8fafc);padding:16px}.std__main{padding:18px 20px;min-width:0}.std__verified{display:inline-flex;align-items:center;gap:8px;margin-bottom:14px;padding:8px 14px;border-radius:10px;background:var(--sbd-r-success-bg, #e7f7ec);color:var(--sbd-r-success-text, #1a7f37);border:1px solid var(--sbd-r-success-line, #b7e6c4);font-weight:700;font-size:12.5px}.std__leg{background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;padding:14px;margin-bottom:12px}.std__leg-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.std__dir{font-size:11.5px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;display:inline-flex;align-items:center;gap:6px}.std__badges{display:flex;gap:5px}.std__badge{font-size:10.5px;font-weight:700;padding:3px 8px;border-radius:999px;background:var(--std-accent-soft, #eff6ff);color:#007ad9}.std__badge--fare{background:var(--sbd-r-warn-bg, #fff3d6);color:var(--sbd-r-warn-text, #a56a00)}.std__operator{font-size:12.5px;color:var(--sbd-r-muted, #64748b);margin-bottom:12px}.std__itin{display:flex;align-items:flex-start}.std__stop{display:flex;flex-direction:column;flex:none;align-items:flex-start}.std__stop--end{align-items:flex-end;text-align:right}.std__node{width:10px;height:10px;border-radius:50%;background:#007ad9;margin-bottom:8px}.std__city{font-size:15px;font-weight:800;line-height:1.15;max-width:130px}.std__station{font-size:10.5px;color:var(--sbd-r-faint, #94a3b8);margin-top:2px;line-height:1.2;max-width:130px}.std__time{font-size:13.5px;font-weight:700;margin-top:4px}.std__date{font-size:11px;color:var(--sbd-r-faint, #94a3b8);margin-top:1px}.std__seg{flex:1;min-width:26px;display:flex;flex-direction:column;align-items:center}.std__bar{position:relative;width:100%;height:2px;background:var(--sbd-r-line, #e2e8f0);margin-top:4px}.std__bar .pi{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:9px;color:#007ad9;background:var(--sbd-r-surface, #fff);padding:0 3px}.std__dur{font-size:9.5px;font-weight:700;background:var(--sbd-r-soft, #f8fafc);border:1px solid var(--sbd-r-line, #e2e8f0);color:var(--sbd-r-muted, #64748b);padding:1px 6px;border-radius:6px;margin-top:7px;white-space:nowrap}.std__box{background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;padding:12px 14px;margin-bottom:12px}.std__box-h{font-size:11.5px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;color:var(--sbd-r-muted, #64748b);display:flex;align-items:center;gap:7px;margin-bottom:10px}.std__n{margin-left:auto;background:var(--sbd-r-sunk, #f1f5f9);color:var(--sbd-r-muted, #64748b);border-radius:999px;padding:1px 8px;font-size:11px}.std__pax{display:flex;align-items:center;gap:10px;padding:7px 0;border-top:1px dashed var(--sbd-r-line, #e2e8f0)}.std__pax:first-of-type{border-top:none}.std__ptag{flex:none;font-size:10px;font-weight:800;text-transform:uppercase;padding:3px 8px;border-radius:6px;background:var(--std-accent-soft, #eff6ff);color:#007ad9}.std__pinfo{min-width:0}.std__pname{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.std__pdoc{font-size:11px;color:var(--sbd-r-faint, #94a3b8);word-break:break-all}.std__pay{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--sbd-r-surface, #fff);border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;padding:12px 14px}.std__pay-l{display:flex;gap:10px;align-items:center}.std__pay-l>.pi{color:#007ad9;font-size:15px}.std__amt{font-size:15px;font-weight:800}.std__pmeta{font-size:11px;color:var(--sbd-r-faint, #94a3b8)}.std__pok{flex:none;font-size:11.5px;font-weight:700;color:var(--sbd-r-success-text, #1a7f37);display:inline-flex;align-items:center;gap:5px}.std__main-h{font-size:15px;font-weight:700;margin-bottom:12px}.std__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-bottom:16px}.std__card{text-align:left;border:1.5px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-surface, #fff);border-radius:13px;padding:12px;cursor:pointer;display:flex;flex-direction:column;gap:3px;font-family:inherit;transition:border-color .12s,box-shadow .12s}.std__card:hover{border-color:var(--std-hover-line, #bfdbfe)}.std__card--on{border-color:#007ad9;box-shadow:0 0 0 3px var(--std-accent-soft, #eff6ff)}.std__card-ico{width:30px;height:30px;border-radius:8px;background:var(--sbd-r-sunk, #f1f5f9);color:var(--sbd-r-muted, #64748b);display:grid;place-items:center;margin-bottom:5px}.std__card--on .std__card-ico{background:var(--std-accent-soft, #eff6ff);color:#007ad9}.std__card-t{font-size:13px;font-weight:700}.std__card--on .std__card-t{color:#007ad9}.std__card-s{font-size:11px;color:var(--sbd-r-faint, #94a3b8)}.std__panel{border:1px solid var(--sbd-r-line, #e2e8f0);border-radius:14px;overflow:hidden}.std__panel-h{padding:12px 16px;border-bottom:1px solid var(--sbd-r-line, #e2e8f0);font-size:13.5px;font-weight:700;background:var(--sbd-r-soft, #f8fafc);display:flex;align-items:center;gap:8px}.std__panel-h .pi{color:#007ad9}.std__panel-h small{font-weight:500;color:var(--sbd-r-faint, #94a3b8)}.std__rows{padding:4px 16px;max-height:40vh;overflow-y:auto}.std__row{display:grid;grid-template-columns:160px 1fr;gap:12px;padding:9px 0;border-bottom:1px dashed var(--sbd-r-line, #e2e8f0)}.std__row:last-child{border-bottom:none}.std__label{font-size:12.5px;font-weight:600;color:var(--sbd-r-muted, #64748b);display:flex;flex-direction:column;gap:2px}.std__label small{font-weight:500;color:var(--sbd-r-faint, #94a3b8)}.std__val{font-size:13.5px;min-width:0}.std__en{white-space:pre-line;word-break:break-word}.std__ar{white-space:pre-line;word-break:break-word;color:var(--sbd-r-muted, #64748b);margin-top:3px}.std__copybar{display:flex;align-items:center;gap:10px;padding:12px 16px;border-top:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-soft, #f8fafc)}.std__hint{font-size:12px;color:var(--sbd-r-faint, #94a3b8)}.std__copied{color:var(--sbd-r-success-text, #1a7f37);font-size:12.5px;font-weight:700}.std__spacer{flex:1}.std__btn{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-surface, #fff);color:var(--sbd-r-ink, #1e2e4a);border-radius:9px;padding:8px 13px;font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit}.std__btn--primary{background:#007ad9;border-color:#007ad9;color:#fff}@media (max-width: 820px){.std__grid{grid-template-columns:minmax(0,1fr)}.std__rail{border-right:none;border-bottom:1px solid var(--sbd-r-line, #e2e8f0)}}.std__qa{margin-bottom:18px}.std__qa-h{font-size:11.5px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:var(--sbd-r-muted, #64748b);margin-bottom:10px}.std__qa-row{display:flex;flex-wrap:wrap;gap:8px}.std__qa-btn{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--sbd-r-line, #e2e8f0);background:var(--sbd-r-surface, #fff);color:var(--sbd-r-muted, #64748b);border-radius:9px;padding:8px 13px;font-size:12.5px;font-weight:600;font-family:inherit;cursor:not-allowed;opacity:.6}:host-context(.dark) .std,:host-context([data-theme=dark]) .std{--sbd-r-bg: #18181b;--sbd-r-surface: #1f1f23;--sbd-r-soft: #27272a;--sbd-r-sunk: #2f2f34;--sbd-r-ink: #f4f4f5;--sbd-r-muted: #a1a1aa;--sbd-r-faint: #8b8b94;--sbd-r-line: #3f3f46;--sbd-r-line-strong: #52525b;--sbd-r-label: #d4d4d8;--sbd-r-input-text: #e4e4e7;--sbd-r-info-bg: #172554;--sbd-r-info-line: #1e3a8a;--sbd-r-info-text: #93c5fd;--sbd-r-warn-bg: #3a2a0e;--sbd-r-warn-line: #6b4a16;--sbd-r-warn-text: #fcd34d;--sbd-r-warn-icon: #f59e0b;--sbd-r-error-bg: #3a1517;--sbd-r-error-line: #7f1d1d;--sbd-r-error-text: #fca5a5;--sbd-r-success-bg: #0f2a1a;--sbd-r-success-line: #166534;--sbd-r-success-text: #86efac;--sbd-r-state-bg: #3a2410;--sbd-r-state-line: #7c3a10;--sbd-r-state-warn-bg: #3a1517;--sbd-r-state-warn-line: #7f1d1d;--sbd-r-green: #4ade80;--sbd-r-expiry-soon: #fbbf24;--sbd-r-accent-soft: color-mix(in srgb, var(--sbd-accent, #6d4aff) 28%, transparent);--std-accent-soft: #1e2a52;--std-hover-line: #3b5ea8}\n"] }]
|
|
5939
|
+
}] });
|
|
5940
|
+
|
|
5847
5941
|
class SbdHotelItineraryCardComponent {
|
|
5848
5942
|
constructor() {
|
|
5849
5943
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -8337,6 +8431,8 @@ class SmartBookingDashboardComponent {
|
|
|
8337
8431
|
/** The Reissue button is shown for flights only, and only when the host enabled the feature. */
|
|
8338
8432
|
this.reissueEnabled = inject(SBD_CONFIG).reissueEnabled;
|
|
8339
8433
|
this.product = signal('flight');
|
|
8434
|
+
/** The product the FCR based parts (cancel, notes, actions) work on. Trains have none of them. */
|
|
8435
|
+
this.fcrProduct = computed(() => (this.product() === 'hotel' ? 'hotel' : 'flight'));
|
|
8340
8436
|
this.bookingId = signal('');
|
|
8341
8437
|
this.bookingStatus = signal('idle');
|
|
8342
8438
|
this.bookingData = signal(null);
|
|
@@ -8347,7 +8443,7 @@ class SmartBookingDashboardComponent {
|
|
|
8347
8443
|
this.reissueDialogOpen = signal(false);
|
|
8348
8444
|
this.selectedActionKey = signal(null);
|
|
8349
8445
|
this.activeContextTab = signal('changes');
|
|
8350
|
-
this.availableActions = computed(() => (this.product() === 'flight' ? FLIGHT_ACTIONS : HOTEL_ACTIONS).filter(a => !a.hidden));
|
|
8446
|
+
this.availableActions = computed(() => (this.product() === 'flight' ? FLIGHT_ACTIONS : this.product() === 'hotel' ? HOTEL_ACTIONS : []).filter(a => !a.hidden));
|
|
8351
8447
|
// Handle both correct spelling (dataVerified) and BE typo (dataVerfied)
|
|
8352
8448
|
this.dataVerified = computed(() => {
|
|
8353
8449
|
const bi = this.bookingData()?.data?.booking_info;
|
|
@@ -8432,13 +8528,31 @@ class SmartBookingDashboardComponent {
|
|
|
8432
8528
|
if (!id)
|
|
8433
8529
|
return;
|
|
8434
8530
|
this.bookingStatus.set('loading');
|
|
8435
|
-
const
|
|
8531
|
+
const product = this.product();
|
|
8532
|
+
if (product === 'train') {
|
|
8533
|
+
this.bookingService.getTrainDetails(id).subscribe({
|
|
8534
|
+
next: (detail) => {
|
|
8535
|
+
const found = detail.configured && (detail.found || !!detail.summary || detail.sections?.length > 0);
|
|
8536
|
+
this.bookingData.set(found ? detail : null);
|
|
8537
|
+
this.bookingStatus.set(found ? 'success' : 'error');
|
|
8538
|
+
if (!found)
|
|
8539
|
+
this.toast.error('Train booking not found. Check the ID and try again.');
|
|
8540
|
+
},
|
|
8541
|
+
error: () => {
|
|
8542
|
+
this.bookingStatus.set('error');
|
|
8543
|
+
this.bookingData.set(null);
|
|
8544
|
+
this.toast.error('Train booking not found. Check the ID and try again.');
|
|
8545
|
+
},
|
|
8546
|
+
});
|
|
8547
|
+
return;
|
|
8548
|
+
}
|
|
8549
|
+
const fields = product === 'hotel'
|
|
8436
8550
|
? ['booking_info', 'hotel_information', 'checkin_checkout', 'room_information']
|
|
8437
8551
|
: 'booking_info';
|
|
8438
|
-
this.bookingService.getBooking(id,
|
|
8552
|
+
this.bookingService.getBooking(id, product, fields).subscribe({
|
|
8439
8553
|
next: ({ data }) => {
|
|
8440
8554
|
// console.log('Booking data received:', data);
|
|
8441
|
-
if (
|
|
8555
|
+
if (product === 'hotel') {
|
|
8442
8556
|
const mappedData = mapToHotelBookingData(data);
|
|
8443
8557
|
this.bookingData.set(mappedData);
|
|
8444
8558
|
}
|
|
@@ -8455,7 +8569,7 @@ class SmartBookingDashboardComponent {
|
|
|
8455
8569
|
});
|
|
8456
8570
|
}
|
|
8457
8571
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SmartBookingDashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8458
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SmartBookingDashboardComponent, isStandalone: true, selector: "app-smart-booking-dashboard", host: { listeners: { "document:keydown": "onKeydown($event)" } }, ngImport: i0, template: "<div class=\"sbd-page\">\n\n <!-- Search bar full-width at top -->\n <div class=\"sbd-page__search\">\n <app-sbd-booking-input\n [(product)]=\"product\"\n [(bookingId)]=\"bookingId\"\n [loading]=\"bookingStatus() === 'loading'\"\n (lookup)=\"lookupBooking()\"\n (brandChanged)=\"onBrandChanged()\"\n />\n </div>\n\n <!-- Hotel: Use dedicated component -->\n @if (product() === 'hotel') {\n <app-sbd-hotel-content\n [bookingData]=\"bookingData()\"\n [bookingStatus]=\"bookingStatus()\"\n [bookingId]=\"bookingId()\"\n [dataVerified]=\"dataVerified()\"\n [bookingNotes]=\"bookingNotes()\"\n [bookingHistory]=\"bookingHistory()\"\n [historyGroups]=\"historyGroups()\"\n [activeContextTab]=\"activeContextTab()\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (addNoteRequested)=\"addNoteDialogOpen.set(true)\"\n (contextTabChanged)=\"activeContextTab.set($event)\"\n />\n }\n\n <!-- Flight: Keep original 2-column layout -->\n @if (product() === 'flight') {\n <div class=\"sbd-page__content\">\n\n <!-- Left: booking context -->\n <aside class=\"sbd-page__aside\">\n\n <!-- Data search at the top of the sidebar -->\n @if (bookingStatus() === 'success') {\n <app-sbd-data-search [data]=\"bookingData()\" />\n <app-sbd-flight-itinerary-card [product]=\"product()\" [data]=\"bookingData()\" [verified]=\"dataVerified()\" />\n }\n\n <!-- Loading skeleton for aside -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:60%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:80%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:50%\"></div>\n </div>\n }\n\n </aside>\n\n <!-- Right: agent workspace -->\n <main class=\"sbd-page__main\">\n\n <!-- Error state -->\n @if (bookingStatus() === 'error') {\n <div class=\"sbd-page__error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n <span>Booking not found. Double-check the ID and product type, then try again.</span>\n </div>\n }\n\n <!-- Content once loaded -->\n @if (bookingStatus() === 'success') {\n\n <!-- Quick actions -->\n <div class=\"sbd-page__quick-actions\">\n <span class=\"sbd-page__quick-label\">Quick actions</span>\n <app-sbd-workflow-actions\n [bookingId]=\"bookingId()\"\n [showReissue]=\"reissueEnabled && product() === 'flight'\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (reissue)=\"reissueDialogOpen.set(true)\"\n />\n </div>\n\n <!-- Action grid -->\n <app-sbd-action-grid\n [actions]=\"availableActions()\"\n [activeKey]=\"selectedActionKey()\"\n (actionSelected)=\"openAction($event)\"\n />\n\n <!-- Context: Notes + Changes tabbed -->\n <div class=\"sbd-page__context\">\n\n <!-- Tab bar -->\n <div class=\"sbd-page__context-tabs\">\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'changes'\"\n (click)=\"activeContextTab.set('changes')\">\n <i class=\"pi pi-history\"></i>\n Changes\n @if (bookingHistory().length > 0) {\n <span class=\"sbd-page__context-pill sbd-page__context-pill--changes\">{{ bookingHistory().length }}</span>\n }\n </button>\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'notes'\"\n (click)=\"activeContextTab.set('notes')\">\n <i class=\"pi pi-comments\"></i>\n Notes\n <span class=\"sbd-page__context-pill sbd-page__context-pill--notes\">{{ bookingNotes().length }}</span>\n </button>\n @if (activeContextTab() === 'notes') {\n <button pButton label=\"Add Note\" icon=\"pi pi-plus\" class=\"p-button-sm p-button-outlined sbd-page__context-add\"\n (click)=\"addNoteDialogOpen.set(true)\">\n </button>\n }\n </div>\n\n <!-- Notes tab -->\n @if (activeContextTab() === 'notes') {\n <div class=\"sbd-page__notes-list\">\n @if (bookingNotes().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-inbox\"></i> No notes yet\n </div>\n }\n @for (note of bookingNotes(); track $index) {\n <div class=\"sbd-page__note\">\n <div class=\"sbd-page__note-top\">\n <span class=\"sbd-page__note-badge sbd-page__note-badge--{{ note.action | lowercase }}\">\n {{ note.action }}\n </span>\n <span class=\"sbd-page__note-author\">\n {{ note.writtenBy?.firstName }} {{ note.writtenBy?.lastName }}\n </span>\n <span class=\"sbd-page__note-date\">{{ fmtNoteDate(note.date) }}</span>\n </div>\n <p class=\"sbd-page__note-msg\">{{ note.message }}</p>\n </div>\n }\n </div>\n }\n\n <!-- Changes tab -->\n @if (activeContextTab() === 'changes') {\n <div class=\"sbd-page__changes-groups\">\n @if (historyGroups().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-check\"></i> No changes recorded\n </div>\n }\n @for (group of historyGroups(); track group.changedAt) {\n <div class=\"sbd-page__changes-group\">\n <div class=\"sbd-page__changes-meta\">\n <i class=\"pi pi-user\"></i>\n <strong>{{ group.changedBy }}</strong>\n <span class=\"sbd-page__changes-sep\">\u00B7</span>\n <span>{{ fmtDate(group.changedAt) }}</span>\n </div>\n <table class=\"sbd-page__changes-table\">\n <thead>\n <tr>\n <th>Field</th>\n <th><i class=\"pi pi-times-circle\"></i> Before</th>\n <th><i class=\"pi pi-check-circle\"></i> After</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of group.entries; track entry.field) {\n <tr>\n <td class=\"sbd-page__changes-field\">{{ formatField(entry.field) }}</td>\n <td class=\"sbd-page__changes-old\">{{ entry.from }}</td>\n <td class=\"sbd-page__changes-new\">{{ entry.to }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n\n </div>\n\n }\n\n <!-- Loading skeleton for main -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:25%\"></div>\n </div>\n }\n\n </main>\n\n </div>\n }\n\n</div>\n\n<app-sbd-cancel-dialog\n [visible]=\"cancelDialogOpen() && product() === 'flight'\"\n [bookingId]=\"bookingId()\"\n [product]=\"product()\"\n [pnrInfo]=\"cancelPnrInfo()\"\n [bookingInfo]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (confirmed)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<almatar-hotel-auto-cancellation\n *ngIf=\"cancelDialogOpen() && product() === 'hotel'\"\n [selectedHotelBooking]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (cancellationSuccess)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<app-sbd-action-dialog\n [visible]=\"actionDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"product()\"\n [actions]=\"availableActions()\"\n [initialKey]=\"selectedActionKey()\"\n [preloadedData]=\"bookingData()\"\n (closed)=\"actionDialogOpen.set(false)\"\n/>\n\n<app-sbd-add-note-dialog\n [visible]=\"addNoteDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"product()\"\n (closed)=\"addNoteDialogOpen.set(false)\"\n/>\n\n<app-sbd-reissue-dialog\n [visible]=\"reissueDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [bookingData]=\"bookingData()\"\n (closed)=\"reissueDialogOpen.set(false)\"\n/>\n", styles: [".sbd-page{display:flex;flex-direction:column;gap:20px;padding:24px;max-width:1400px;min-height:100%}.sbd-page__search{width:100%}.sbd-page__error{display:flex;align-items:center;gap:10px;padding:16px 20px;background:#fef2f2;border:1px solid rgba(239,68,68,.2);border-radius:10px;color:#ef4444;font-size:13.5px;font-weight:500}.sbd-page__error i{font-size:18px}.sbd-page__content{display:grid;grid-template-columns:320px 1fr;gap:20px;align-items:start}@media (max-width: 960px){.sbd-page__content{grid-template-columns:1fr}}.sbd-page__aside{display:flex;flex-direction:column;gap:16px;position:sticky;top:20px}.sbd-page__main{display:flex;flex-direction:column;gap:16px}.sbd-page__quick-actions{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;flex-wrap:wrap}.sbd-page__quick-label{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#94a3b8;flex-shrink:0}.sbd-page__context{background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.sbd-page__context-tabs{display:flex;align-items:center;border-bottom:1px solid #e2e8f0;background:#f8fafc}.sbd-page__context-add{margin-left:auto;margin-right:10px}.sbd-page__context-tab{display:flex;align-items:center;gap:7px;padding:11px 20px;font-size:13px;font-weight:600;color:#64748b;background:transparent;border:none;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color .15s,border-color .15s}.sbd-page__context-tab .pi{font-size:13px}.sbd-page__context-tab:hover{color:#334155}.sbd-page__context-tab--active{color:#1e2e4a;border-bottom-color:var(--sbd-primary, #3b82f6);background:#fff}.sbd-page__context-pill{font-size:10.5px;font-weight:700;padding:1px 7px;border-radius:10px}.sbd-page__context-pill--notes{background:#e0e7ff;color:#4338ca}.sbd-page__context-pill--changes{background:#fef3c7;color:#b45309}.sbd-page__context-empty{display:flex;align-items:center;gap:8px;padding:24px 20px;font-size:13px;color:#94a3b8}.sbd-page__context-empty .pi{font-size:16px}.sbd-page__notes-list{display:flex;flex-direction:column}.sbd-page__note{padding:12px 16px;border-bottom:1px solid #f1f5f9;display:flex;flex-direction:column;gap:6px}.sbd-page__note:last-child{border-bottom:none}.sbd-page__note-top{display:flex;align-items:center;gap:8px}.sbd-page__note-badge{font-size:10px;font-weight:800;letter-spacing:.05em;padding:2px 8px;border-radius:10px;text-transform:uppercase;flex-shrink:0}.sbd-page__note-badge--general{background:#dbeafe;color:#1d4ed8}.sbd-page__note-badge--remark{background:#fef3c7;color:#b45309}.sbd-page__note-badge--feedback{background:#f3e8ff;color:#7e22ce}.sbd-page__note-author{font-size:12px;font-weight:700;color:#1e2e4a}.sbd-page__note-date{margin-left:auto;font-size:11px;color:#94a3b8;white-space:nowrap}.sbd-page__note-msg{margin:0;font-size:13px;color:#334155;line-height:1.55}.sbd-page__changes-groups{display:flex;flex-direction:column}.sbd-page__changes-group{border-bottom:1px solid #f1f5f9}.sbd-page__changes-group:last-child{border-bottom:none}.sbd-page__changes-meta{display:flex;align-items:center;gap:6px;padding:8px 16px;background:#fafafa;border-bottom:1px solid #f1f5f9;font-size:11.5px;color:#64748b}.sbd-page__changes-meta strong{font-weight:700;color:#334155}.sbd-page__changes-meta .pi{font-size:11px;color:#94a3b8}.sbd-page__changes-sep{color:#cbd5e1}.sbd-page__changes-table{width:100%;border-collapse:collapse;font-size:12.5px}.sbd-page__changes-table thead tr{background:#f8fafc;border-bottom:1px solid #e2e8f0}.sbd-page__changes-table thead th{padding:7px 14px;text-align:left;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:#64748b;white-space:nowrap}.sbd-page__changes-table thead th .pi{margin-right:4px;font-size:10px}.sbd-page__changes-table thead th:nth-child(2){color:#dc2626}.sbd-page__changes-table thead th:nth-child(3){color:#16a34a}.sbd-page__changes-table tbody tr{border-bottom:1px solid #f1f5f9}.sbd-page__changes-table tbody tr:last-child{border-bottom:none}.sbd-page__changes-table tbody tr:hover{background:#f8fafc}.sbd-page__changes-table tbody td{padding:8px 14px;vertical-align:top;word-break:break-word}.sbd-page__changes-field{font-size:12px;font-weight:600;color:#475569;width:28%}.sbd-page__changes-old{color:#dc2626;font-weight:500;text-decoration:line-through;opacity:.8;width:36%}.sbd-page__changes-new{color:#16a34a;font-weight:700;width:36%}.sbd-page__content--loading{opacity:.6;pointer-events:none}.sbd-page__skeleton-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:20px;display:flex;flex-direction:column;gap:12px}.sbd-page__skeleton-row{height:13px;background:#f1f5f9;border-radius:6px;animation:sbd-shimmer 1.4s ease-in-out infinite}.sbd-page__skeleton-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:8px}.sbd-page__skeleton-chip{height:56px;background:#f1f5f9;border-radius:8px;animation:sbd-shimmer 1.4s ease-in-out infinite}@keyframes sbd-shimmer{0%,to{opacity:1}50%{opacity:.45}}@keyframes sbd-fadein{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "component", type: SbdBookingInputComponent, selector: "app-sbd-booking-input", inputs: ["loading", "product", "bookingId"], outputs: ["productChange", "bookingIdChange", "lookup", "brandChanged"] }, { kind: "component", type: SbdWorkflowActionsComponent, selector: "app-sbd-workflow-actions", inputs: ["bookingId", "showReissue"], outputs: ["cancelBooking", "reissue"] }, { kind: "component", type: SbdActionGridComponent, selector: "app-sbd-action-grid", inputs: ["actions", "activeKey", "loadingKey"], outputs: ["actionSelected"] }, { kind: "component", type: SbdCancelDialogComponent, selector: "app-sbd-cancel-dialog", inputs: ["visible", "bookingId", "product", "pnrInfo", "bookingInfo"], outputs: ["closed", "confirmed"] }, { kind: "component", type: AlmatarHotelAutoCancellationComponent, selector: "almatar-hotel-auto-cancellation", inputs: ["selectedHotelBooking"], outputs: ["closed", "cancellationSuccess", "nonRefundableSubmitSuccess", "chatWithAgent"] }, { kind: "component", type: SbdFlightItineraryCardComponent, selector: "app-sbd-flight-itinerary-card", inputs: ["data", "product", "verified"] }, { kind: "component", type: SbdDataSearchComponent, selector: "app-sbd-data-search", inputs: ["data"] }, { kind: "component", type: SbdActionDialogComponent, selector: "app-sbd-action-dialog", inputs: ["visible", "bookingId", "product", "actions", "initialKey", "preloadedData"], outputs: ["closed"] }, { kind: "component", type: SbdAddNoteDialogComponent, selector: "app-sbd-add-note-dialog", inputs: ["visible", "bookingId", "product"], outputs: ["closed"] }, { kind: "component", type: SbdReissueDialogComponent, selector: "app-sbd-reissue-dialog", inputs: ["visible", "bookingId", "bookingData"], outputs: ["closed"] }, { kind: "component", type: SbdHotelContentComponent, selector: "app-sbd-hotel-content", inputs: ["bookingData", "bookingStatus", "bookingId", "dataVerified", "bookingNotes", "bookingHistory", "historyGroups", "activeContextTab"], outputs: ["cancelBooking", "addNoteRequested", "contextTabChanged"] }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading"] }] }); }
|
|
8572
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SmartBookingDashboardComponent, isStandalone: true, selector: "app-smart-booking-dashboard", host: { listeners: { "document:keydown": "onKeydown($event)" } }, ngImport: i0, template: "<div class=\"sbd-page\">\n\n <!-- Search bar full-width at top -->\n <div class=\"sbd-page__search\">\n <app-sbd-booking-input\n [(product)]=\"product\"\n [(bookingId)]=\"bookingId\"\n [loading]=\"bookingStatus() === 'loading'\"\n (lookup)=\"lookupBooking()\"\n (brandChanged)=\"onBrandChanged()\"\n />\n </div>\n\n <!-- Hotel: Use dedicated component -->\n @if (product() === 'hotel') {\n <app-sbd-hotel-content\n [bookingData]=\"bookingData()\"\n [bookingStatus]=\"bookingStatus()\"\n [bookingId]=\"bookingId()\"\n [dataVerified]=\"dataVerified()\"\n [bookingNotes]=\"bookingNotes()\"\n [bookingHistory]=\"bookingHistory()\"\n [historyGroups]=\"historyGroups()\"\n [activeContextTab]=\"activeContextTab()\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (addNoteRequested)=\"addNoteDialogOpen.set(true)\"\n (contextTabChanged)=\"activeContextTab.set($event)\"\n />\n }\n\n <!-- Train: read-only booking details -->\n @if (product() === 'train') {\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:60%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:80%\"></div>\n </div>\n }\n @if (bookingStatus() === 'error') {\n <div class=\"sbd-page__error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n <span>Train booking not found. Double-check the ID, then try again.</span>\n </div>\n }\n @if (bookingStatus() === 'success' && bookingData()) {\n <app-sbd-train-details [detail]=\"bookingData()\" [bookingId]=\"bookingId()\" />\n }\n }\n\n <!-- Flight: Keep original 2-column layout -->\n @if (product() === 'flight') {\n <div class=\"sbd-page__content\">\n\n <!-- Left: booking context -->\n <aside class=\"sbd-page__aside\">\n\n <!-- Data search at the top of the sidebar -->\n @if (bookingStatus() === 'success') {\n <app-sbd-data-search [data]=\"bookingData()\" />\n <app-sbd-flight-itinerary-card [product]=\"fcrProduct()\" [data]=\"bookingData()\" [verified]=\"dataVerified()\" />\n }\n\n <!-- Loading skeleton for aside -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:60%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:80%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:50%\"></div>\n </div>\n }\n\n </aside>\n\n <!-- Right: agent workspace -->\n <main class=\"sbd-page__main\">\n\n <!-- Error state -->\n @if (bookingStatus() === 'error') {\n <div class=\"sbd-page__error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n <span>Booking not found. Double-check the ID and product type, then try again.</span>\n </div>\n }\n\n <!-- Content once loaded -->\n @if (bookingStatus() === 'success') {\n\n <!-- Quick actions -->\n <div class=\"sbd-page__quick-actions\">\n <span class=\"sbd-page__quick-label\">Quick actions</span>\n <app-sbd-workflow-actions\n [bookingId]=\"bookingId()\"\n [showReissue]=\"reissueEnabled && product() === 'flight'\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (reissue)=\"reissueDialogOpen.set(true)\"\n />\n </div>\n\n <!-- Action grid -->\n <app-sbd-action-grid\n [actions]=\"availableActions()\"\n [activeKey]=\"selectedActionKey()\"\n (actionSelected)=\"openAction($event)\"\n />\n\n <!-- Context: Notes + Changes tabbed -->\n <div class=\"sbd-page__context\">\n\n <!-- Tab bar -->\n <div class=\"sbd-page__context-tabs\">\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'changes'\"\n (click)=\"activeContextTab.set('changes')\">\n <i class=\"pi pi-history\"></i>\n Changes\n @if (bookingHistory().length > 0) {\n <span class=\"sbd-page__context-pill sbd-page__context-pill--changes\">{{ bookingHistory().length }}</span>\n }\n </button>\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'notes'\"\n (click)=\"activeContextTab.set('notes')\">\n <i class=\"pi pi-comments\"></i>\n Notes\n <span class=\"sbd-page__context-pill sbd-page__context-pill--notes\">{{ bookingNotes().length }}</span>\n </button>\n @if (activeContextTab() === 'notes') {\n <button pButton label=\"Add Note\" icon=\"pi pi-plus\" class=\"p-button-sm p-button-outlined sbd-page__context-add\"\n (click)=\"addNoteDialogOpen.set(true)\">\n </button>\n }\n </div>\n\n <!-- Notes tab -->\n @if (activeContextTab() === 'notes') {\n <div class=\"sbd-page__notes-list\">\n @if (bookingNotes().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-inbox\"></i> No notes yet\n </div>\n }\n @for (note of bookingNotes(); track $index) {\n <div class=\"sbd-page__note\">\n <div class=\"sbd-page__note-top\">\n <span class=\"sbd-page__note-badge sbd-page__note-badge--{{ note.action | lowercase }}\">\n {{ note.action }}\n </span>\n <span class=\"sbd-page__note-author\">\n {{ note.writtenBy?.firstName }} {{ note.writtenBy?.lastName }}\n </span>\n <span class=\"sbd-page__note-date\">{{ fmtNoteDate(note.date) }}</span>\n </div>\n <p class=\"sbd-page__note-msg\">{{ note.message }}</p>\n </div>\n }\n </div>\n }\n\n <!-- Changes tab -->\n @if (activeContextTab() === 'changes') {\n <div class=\"sbd-page__changes-groups\">\n @if (historyGroups().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-check\"></i> No changes recorded\n </div>\n }\n @for (group of historyGroups(); track group.changedAt) {\n <div class=\"sbd-page__changes-group\">\n <div class=\"sbd-page__changes-meta\">\n <i class=\"pi pi-user\"></i>\n <strong>{{ group.changedBy }}</strong>\n <span class=\"sbd-page__changes-sep\">\u00B7</span>\n <span>{{ fmtDate(group.changedAt) }}</span>\n </div>\n <table class=\"sbd-page__changes-table\">\n <thead>\n <tr>\n <th>Field</th>\n <th><i class=\"pi pi-times-circle\"></i> Before</th>\n <th><i class=\"pi pi-check-circle\"></i> After</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of group.entries; track entry.field) {\n <tr>\n <td class=\"sbd-page__changes-field\">{{ formatField(entry.field) }}</td>\n <td class=\"sbd-page__changes-old\">{{ entry.from }}</td>\n <td class=\"sbd-page__changes-new\">{{ entry.to }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n\n </div>\n\n }\n\n <!-- Loading skeleton for main -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:25%\"></div>\n </div>\n }\n\n </main>\n\n </div>\n }\n\n</div>\n\n<app-sbd-cancel-dialog\n [visible]=\"cancelDialogOpen() && product() === 'flight'\"\n [bookingId]=\"bookingId()\"\n [product]=\"fcrProduct()\"\n [pnrInfo]=\"cancelPnrInfo()\"\n [bookingInfo]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (confirmed)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<almatar-hotel-auto-cancellation\n *ngIf=\"cancelDialogOpen() && product() === 'hotel'\"\n [selectedHotelBooking]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (cancellationSuccess)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<app-sbd-action-dialog\n [visible]=\"actionDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"fcrProduct()\"\n [actions]=\"availableActions()\"\n [initialKey]=\"selectedActionKey()\"\n [preloadedData]=\"bookingData()\"\n (closed)=\"actionDialogOpen.set(false)\"\n/>\n\n<app-sbd-add-note-dialog\n [visible]=\"addNoteDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"fcrProduct()\"\n (closed)=\"addNoteDialogOpen.set(false)\"\n/>\n\n<app-sbd-reissue-dialog\n [visible]=\"reissueDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [bookingData]=\"bookingData()\"\n (closed)=\"reissueDialogOpen.set(false)\"\n/>\n", styles: [".sbd-page{display:flex;flex-direction:column;gap:20px;padding:24px;max-width:1400px;min-height:100%}.sbd-page__search{width:100%}.sbd-page__error{display:flex;align-items:center;gap:10px;padding:16px 20px;background:#fef2f2;border:1px solid rgba(239,68,68,.2);border-radius:10px;color:#ef4444;font-size:13.5px;font-weight:500}.sbd-page__error i{font-size:18px}.sbd-page__content{display:grid;grid-template-columns:320px 1fr;gap:20px;align-items:start}@media (max-width: 960px){.sbd-page__content{grid-template-columns:1fr}}.sbd-page__aside{display:flex;flex-direction:column;gap:16px;position:sticky;top:20px}.sbd-page__main{display:flex;flex-direction:column;gap:16px}.sbd-page__quick-actions{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;flex-wrap:wrap}.sbd-page__quick-label{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#94a3b8;flex-shrink:0}.sbd-page__context{background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.sbd-page__context-tabs{display:flex;align-items:center;border-bottom:1px solid #e2e8f0;background:#f8fafc}.sbd-page__context-add{margin-left:auto;margin-right:10px}.sbd-page__context-tab{display:flex;align-items:center;gap:7px;padding:11px 20px;font-size:13px;font-weight:600;color:#64748b;background:transparent;border:none;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color .15s,border-color .15s}.sbd-page__context-tab .pi{font-size:13px}.sbd-page__context-tab:hover{color:#334155}.sbd-page__context-tab--active{color:#1e2e4a;border-bottom-color:var(--sbd-primary, #3b82f6);background:#fff}.sbd-page__context-pill{font-size:10.5px;font-weight:700;padding:1px 7px;border-radius:10px}.sbd-page__context-pill--notes{background:#e0e7ff;color:#4338ca}.sbd-page__context-pill--changes{background:#fef3c7;color:#b45309}.sbd-page__context-empty{display:flex;align-items:center;gap:8px;padding:24px 20px;font-size:13px;color:#94a3b8}.sbd-page__context-empty .pi{font-size:16px}.sbd-page__notes-list{display:flex;flex-direction:column}.sbd-page__note{padding:12px 16px;border-bottom:1px solid #f1f5f9;display:flex;flex-direction:column;gap:6px}.sbd-page__note:last-child{border-bottom:none}.sbd-page__note-top{display:flex;align-items:center;gap:8px}.sbd-page__note-badge{font-size:10px;font-weight:800;letter-spacing:.05em;padding:2px 8px;border-radius:10px;text-transform:uppercase;flex-shrink:0}.sbd-page__note-badge--general{background:#dbeafe;color:#1d4ed8}.sbd-page__note-badge--remark{background:#fef3c7;color:#b45309}.sbd-page__note-badge--feedback{background:#f3e8ff;color:#7e22ce}.sbd-page__note-author{font-size:12px;font-weight:700;color:#1e2e4a}.sbd-page__note-date{margin-left:auto;font-size:11px;color:#94a3b8;white-space:nowrap}.sbd-page__note-msg{margin:0;font-size:13px;color:#334155;line-height:1.55}.sbd-page__changes-groups{display:flex;flex-direction:column}.sbd-page__changes-group{border-bottom:1px solid #f1f5f9}.sbd-page__changes-group:last-child{border-bottom:none}.sbd-page__changes-meta{display:flex;align-items:center;gap:6px;padding:8px 16px;background:#fafafa;border-bottom:1px solid #f1f5f9;font-size:11.5px;color:#64748b}.sbd-page__changes-meta strong{font-weight:700;color:#334155}.sbd-page__changes-meta .pi{font-size:11px;color:#94a3b8}.sbd-page__changes-sep{color:#cbd5e1}.sbd-page__changes-table{width:100%;border-collapse:collapse;font-size:12.5px}.sbd-page__changes-table thead tr{background:#f8fafc;border-bottom:1px solid #e2e8f0}.sbd-page__changes-table thead th{padding:7px 14px;text-align:left;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:#64748b;white-space:nowrap}.sbd-page__changes-table thead th .pi{margin-right:4px;font-size:10px}.sbd-page__changes-table thead th:nth-child(2){color:#dc2626}.sbd-page__changes-table thead th:nth-child(3){color:#16a34a}.sbd-page__changes-table tbody tr{border-bottom:1px solid #f1f5f9}.sbd-page__changes-table tbody tr:last-child{border-bottom:none}.sbd-page__changes-table tbody tr:hover{background:#f8fafc}.sbd-page__changes-table tbody td{padding:8px 14px;vertical-align:top;word-break:break-word}.sbd-page__changes-field{font-size:12px;font-weight:600;color:#475569;width:28%}.sbd-page__changes-old{color:#dc2626;font-weight:500;text-decoration:line-through;opacity:.8;width:36%}.sbd-page__changes-new{color:#16a34a;font-weight:700;width:36%}.sbd-page__content--loading{opacity:.6;pointer-events:none}.sbd-page__skeleton-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:20px;display:flex;flex-direction:column;gap:12px}.sbd-page__skeleton-row{height:13px;background:#f1f5f9;border-radius:6px;animation:sbd-shimmer 1.4s ease-in-out infinite}.sbd-page__skeleton-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:8px}.sbd-page__skeleton-chip{height:56px;background:#f1f5f9;border-radius:8px;animation:sbd-shimmer 1.4s ease-in-out infinite}@keyframes sbd-shimmer{0%,to{opacity:1}50%{opacity:.45}}@keyframes sbd-fadein{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "component", type: SbdBookingInputComponent, selector: "app-sbd-booking-input", inputs: ["loading", "product", "bookingId"], outputs: ["productChange", "bookingIdChange", "lookup", "brandChanged"] }, { kind: "component", type: SbdWorkflowActionsComponent, selector: "app-sbd-workflow-actions", inputs: ["bookingId", "showReissue"], outputs: ["cancelBooking", "reissue"] }, { kind: "component", type: SbdActionGridComponent, selector: "app-sbd-action-grid", inputs: ["actions", "activeKey", "loadingKey"], outputs: ["actionSelected"] }, { kind: "component", type: SbdCancelDialogComponent, selector: "app-sbd-cancel-dialog", inputs: ["visible", "bookingId", "product", "pnrInfo", "bookingInfo"], outputs: ["closed", "confirmed"] }, { kind: "component", type: AlmatarHotelAutoCancellationComponent, selector: "almatar-hotel-auto-cancellation", inputs: ["selectedHotelBooking"], outputs: ["closed", "cancellationSuccess", "nonRefundableSubmitSuccess", "chatWithAgent"] }, { kind: "component", type: SbdFlightItineraryCardComponent, selector: "app-sbd-flight-itinerary-card", inputs: ["data", "product", "verified"] }, { kind: "component", type: SbdDataSearchComponent, selector: "app-sbd-data-search", inputs: ["data"] }, { kind: "component", type: SbdActionDialogComponent, selector: "app-sbd-action-dialog", inputs: ["visible", "bookingId", "product", "actions", "initialKey", "preloadedData"], outputs: ["closed"] }, { kind: "component", type: SbdAddNoteDialogComponent, selector: "app-sbd-add-note-dialog", inputs: ["visible", "bookingId", "product"], outputs: ["closed"] }, { kind: "component", type: SbdReissueDialogComponent, selector: "app-sbd-reissue-dialog", inputs: ["visible", "bookingId", "bookingData"], outputs: ["closed"] }, { kind: "component", type: SbdHotelContentComponent, selector: "app-sbd-hotel-content", inputs: ["bookingData", "bookingStatus", "bookingId", "dataVerified", "bookingNotes", "bookingHistory", "historyGroups", "activeContextTab"], outputs: ["cancelBooking", "addNoteRequested", "contextTabChanged"] }, { kind: "component", type: SbdTrainDetailsComponent, selector: "app-sbd-train-details", inputs: ["detail", "bookingId"] }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "label", "icon", "loading"] }] }); }
|
|
8459
8573
|
}
|
|
8460
8574
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SmartBookingDashboardComponent, decorators: [{
|
|
8461
8575
|
type: Component,
|
|
@@ -8472,10 +8586,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
8472
8586
|
SbdAddNoteDialogComponent,
|
|
8473
8587
|
SbdReissueDialogComponent,
|
|
8474
8588
|
SbdHotelContentComponent,
|
|
8589
|
+
SbdTrainDetailsComponent,
|
|
8475
8590
|
LowerCasePipe,
|
|
8476
8591
|
NgIf,
|
|
8477
8592
|
ButtonModule,
|
|
8478
|
-
], template: "<div class=\"sbd-page\">\n\n <!-- Search bar full-width at top -->\n <div class=\"sbd-page__search\">\n <app-sbd-booking-input\n [(product)]=\"product\"\n [(bookingId)]=\"bookingId\"\n [loading]=\"bookingStatus() === 'loading'\"\n (lookup)=\"lookupBooking()\"\n (brandChanged)=\"onBrandChanged()\"\n />\n </div>\n\n <!-- Hotel: Use dedicated component -->\n @if (product() === 'hotel') {\n <app-sbd-hotel-content\n [bookingData]=\"bookingData()\"\n [bookingStatus]=\"bookingStatus()\"\n [bookingId]=\"bookingId()\"\n [dataVerified]=\"dataVerified()\"\n [bookingNotes]=\"bookingNotes()\"\n [bookingHistory]=\"bookingHistory()\"\n [historyGroups]=\"historyGroups()\"\n [activeContextTab]=\"activeContextTab()\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (addNoteRequested)=\"addNoteDialogOpen.set(true)\"\n (contextTabChanged)=\"activeContextTab.set($event)\"\n />\n }\n\n <!-- Flight: Keep original 2-column layout -->\n @if (product() === 'flight') {\n <div class=\"sbd-page__content\">\n\n <!-- Left: booking context -->\n <aside class=\"sbd-page__aside\">\n\n <!-- Data search at the top of the sidebar -->\n @if (bookingStatus() === 'success') {\n <app-sbd-data-search [data]=\"bookingData()\" />\n <app-sbd-flight-itinerary-card [product]=\"product()\" [data]=\"bookingData()\" [verified]=\"dataVerified()\" />\n }\n\n <!-- Loading skeleton for aside -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:60%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:80%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:50%\"></div>\n </div>\n }\n\n </aside>\n\n <!-- Right: agent workspace -->\n <main class=\"sbd-page__main\">\n\n <!-- Error state -->\n @if (bookingStatus() === 'error') {\n <div class=\"sbd-page__error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n <span>Booking not found. Double-check the ID and product type, then try again.</span>\n </div>\n }\n\n <!-- Content once loaded -->\n @if (bookingStatus() === 'success') {\n\n <!-- Quick actions -->\n <div class=\"sbd-page__quick-actions\">\n <span class=\"sbd-page__quick-label\">Quick actions</span>\n <app-sbd-workflow-actions\n [bookingId]=\"bookingId()\"\n [showReissue]=\"reissueEnabled && product() === 'flight'\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (reissue)=\"reissueDialogOpen.set(true)\"\n />\n </div>\n\n <!-- Action grid -->\n <app-sbd-action-grid\n [actions]=\"availableActions()\"\n [activeKey]=\"selectedActionKey()\"\n (actionSelected)=\"openAction($event)\"\n />\n\n <!-- Context: Notes + Changes tabbed -->\n <div class=\"sbd-page__context\">\n\n <!-- Tab bar -->\n <div class=\"sbd-page__context-tabs\">\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'changes'\"\n (click)=\"activeContextTab.set('changes')\">\n <i class=\"pi pi-history\"></i>\n Changes\n @if (bookingHistory().length > 0) {\n <span class=\"sbd-page__context-pill sbd-page__context-pill--changes\">{{ bookingHistory().length }}</span>\n }\n </button>\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'notes'\"\n (click)=\"activeContextTab.set('notes')\">\n <i class=\"pi pi-comments\"></i>\n Notes\n <span class=\"sbd-page__context-pill sbd-page__context-pill--notes\">{{ bookingNotes().length }}</span>\n </button>\n @if (activeContextTab() === 'notes') {\n <button pButton label=\"Add Note\" icon=\"pi pi-plus\" class=\"p-button-sm p-button-outlined sbd-page__context-add\"\n (click)=\"addNoteDialogOpen.set(true)\">\n </button>\n }\n </div>\n\n <!-- Notes tab -->\n @if (activeContextTab() === 'notes') {\n <div class=\"sbd-page__notes-list\">\n @if (bookingNotes().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-inbox\"></i> No notes yet\n </div>\n }\n @for (note of bookingNotes(); track $index) {\n <div class=\"sbd-page__note\">\n <div class=\"sbd-page__note-top\">\n <span class=\"sbd-page__note-badge sbd-page__note-badge--{{ note.action | lowercase }}\">\n {{ note.action }}\n </span>\n <span class=\"sbd-page__note-author\">\n {{ note.writtenBy?.firstName }} {{ note.writtenBy?.lastName }}\n </span>\n <span class=\"sbd-page__note-date\">{{ fmtNoteDate(note.date) }}</span>\n </div>\n <p class=\"sbd-page__note-msg\">{{ note.message }}</p>\n </div>\n }\n </div>\n }\n\n <!-- Changes tab -->\n @if (activeContextTab() === 'changes') {\n <div class=\"sbd-page__changes-groups\">\n @if (historyGroups().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-check\"></i> No changes recorded\n </div>\n }\n @for (group of historyGroups(); track group.changedAt) {\n <div class=\"sbd-page__changes-group\">\n <div class=\"sbd-page__changes-meta\">\n <i class=\"pi pi-user\"></i>\n <strong>{{ group.changedBy }}</strong>\n <span class=\"sbd-page__changes-sep\">\u00B7</span>\n <span>{{ fmtDate(group.changedAt) }}</span>\n </div>\n <table class=\"sbd-page__changes-table\">\n <thead>\n <tr>\n <th>Field</th>\n <th><i class=\"pi pi-times-circle\"></i> Before</th>\n <th><i class=\"pi pi-check-circle\"></i> After</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of group.entries; track entry.field) {\n <tr>\n <td class=\"sbd-page__changes-field\">{{ formatField(entry.field) }}</td>\n <td class=\"sbd-page__changes-old\">{{ entry.from }}</td>\n <td class=\"sbd-page__changes-new\">{{ entry.to }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n\n </div>\n\n }\n\n <!-- Loading skeleton for main -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:25%\"></div>\n </div>\n }\n\n </main>\n\n </div>\n }\n\n</div>\n\n<app-sbd-cancel-dialog\n [visible]=\"cancelDialogOpen() && product() === 'flight'\"\n [bookingId]=\"bookingId()\"\n [product]=\"product()\"\n [pnrInfo]=\"cancelPnrInfo()\"\n [bookingInfo]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (confirmed)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<almatar-hotel-auto-cancellation\n *ngIf=\"cancelDialogOpen() && product() === 'hotel'\"\n [selectedHotelBooking]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (cancellationSuccess)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<app-sbd-action-dialog\n [visible]=\"actionDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"product()\"\n [actions]=\"availableActions()\"\n [initialKey]=\"selectedActionKey()\"\n [preloadedData]=\"bookingData()\"\n (closed)=\"actionDialogOpen.set(false)\"\n/>\n\n<app-sbd-add-note-dialog\n [visible]=\"addNoteDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"product()\"\n (closed)=\"addNoteDialogOpen.set(false)\"\n/>\n\n<app-sbd-reissue-dialog\n [visible]=\"reissueDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [bookingData]=\"bookingData()\"\n (closed)=\"reissueDialogOpen.set(false)\"\n/>\n", styles: [".sbd-page{display:flex;flex-direction:column;gap:20px;padding:24px;max-width:1400px;min-height:100%}.sbd-page__search{width:100%}.sbd-page__error{display:flex;align-items:center;gap:10px;padding:16px 20px;background:#fef2f2;border:1px solid rgba(239,68,68,.2);border-radius:10px;color:#ef4444;font-size:13.5px;font-weight:500}.sbd-page__error i{font-size:18px}.sbd-page__content{display:grid;grid-template-columns:320px 1fr;gap:20px;align-items:start}@media (max-width: 960px){.sbd-page__content{grid-template-columns:1fr}}.sbd-page__aside{display:flex;flex-direction:column;gap:16px;position:sticky;top:20px}.sbd-page__main{display:flex;flex-direction:column;gap:16px}.sbd-page__quick-actions{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;flex-wrap:wrap}.sbd-page__quick-label{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#94a3b8;flex-shrink:0}.sbd-page__context{background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.sbd-page__context-tabs{display:flex;align-items:center;border-bottom:1px solid #e2e8f0;background:#f8fafc}.sbd-page__context-add{margin-left:auto;margin-right:10px}.sbd-page__context-tab{display:flex;align-items:center;gap:7px;padding:11px 20px;font-size:13px;font-weight:600;color:#64748b;background:transparent;border:none;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color .15s,border-color .15s}.sbd-page__context-tab .pi{font-size:13px}.sbd-page__context-tab:hover{color:#334155}.sbd-page__context-tab--active{color:#1e2e4a;border-bottom-color:var(--sbd-primary, #3b82f6);background:#fff}.sbd-page__context-pill{font-size:10.5px;font-weight:700;padding:1px 7px;border-radius:10px}.sbd-page__context-pill--notes{background:#e0e7ff;color:#4338ca}.sbd-page__context-pill--changes{background:#fef3c7;color:#b45309}.sbd-page__context-empty{display:flex;align-items:center;gap:8px;padding:24px 20px;font-size:13px;color:#94a3b8}.sbd-page__context-empty .pi{font-size:16px}.sbd-page__notes-list{display:flex;flex-direction:column}.sbd-page__note{padding:12px 16px;border-bottom:1px solid #f1f5f9;display:flex;flex-direction:column;gap:6px}.sbd-page__note:last-child{border-bottom:none}.sbd-page__note-top{display:flex;align-items:center;gap:8px}.sbd-page__note-badge{font-size:10px;font-weight:800;letter-spacing:.05em;padding:2px 8px;border-radius:10px;text-transform:uppercase;flex-shrink:0}.sbd-page__note-badge--general{background:#dbeafe;color:#1d4ed8}.sbd-page__note-badge--remark{background:#fef3c7;color:#b45309}.sbd-page__note-badge--feedback{background:#f3e8ff;color:#7e22ce}.sbd-page__note-author{font-size:12px;font-weight:700;color:#1e2e4a}.sbd-page__note-date{margin-left:auto;font-size:11px;color:#94a3b8;white-space:nowrap}.sbd-page__note-msg{margin:0;font-size:13px;color:#334155;line-height:1.55}.sbd-page__changes-groups{display:flex;flex-direction:column}.sbd-page__changes-group{border-bottom:1px solid #f1f5f9}.sbd-page__changes-group:last-child{border-bottom:none}.sbd-page__changes-meta{display:flex;align-items:center;gap:6px;padding:8px 16px;background:#fafafa;border-bottom:1px solid #f1f5f9;font-size:11.5px;color:#64748b}.sbd-page__changes-meta strong{font-weight:700;color:#334155}.sbd-page__changes-meta .pi{font-size:11px;color:#94a3b8}.sbd-page__changes-sep{color:#cbd5e1}.sbd-page__changes-table{width:100%;border-collapse:collapse;font-size:12.5px}.sbd-page__changes-table thead tr{background:#f8fafc;border-bottom:1px solid #e2e8f0}.sbd-page__changes-table thead th{padding:7px 14px;text-align:left;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:#64748b;white-space:nowrap}.sbd-page__changes-table thead th .pi{margin-right:4px;font-size:10px}.sbd-page__changes-table thead th:nth-child(2){color:#dc2626}.sbd-page__changes-table thead th:nth-child(3){color:#16a34a}.sbd-page__changes-table tbody tr{border-bottom:1px solid #f1f5f9}.sbd-page__changes-table tbody tr:last-child{border-bottom:none}.sbd-page__changes-table tbody tr:hover{background:#f8fafc}.sbd-page__changes-table tbody td{padding:8px 14px;vertical-align:top;word-break:break-word}.sbd-page__changes-field{font-size:12px;font-weight:600;color:#475569;width:28%}.sbd-page__changes-old{color:#dc2626;font-weight:500;text-decoration:line-through;opacity:.8;width:36%}.sbd-page__changes-new{color:#16a34a;font-weight:700;width:36%}.sbd-page__content--loading{opacity:.6;pointer-events:none}.sbd-page__skeleton-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:20px;display:flex;flex-direction:column;gap:12px}.sbd-page__skeleton-row{height:13px;background:#f1f5f9;border-radius:6px;animation:sbd-shimmer 1.4s ease-in-out infinite}.sbd-page__skeleton-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:8px}.sbd-page__skeleton-chip{height:56px;background:#f1f5f9;border-radius:8px;animation:sbd-shimmer 1.4s ease-in-out infinite}@keyframes sbd-shimmer{0%,to{opacity:1}50%{opacity:.45}}@keyframes sbd-fadein{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
|
|
8593
|
+
], template: "<div class=\"sbd-page\">\n\n <!-- Search bar full-width at top -->\n <div class=\"sbd-page__search\">\n <app-sbd-booking-input\n [(product)]=\"product\"\n [(bookingId)]=\"bookingId\"\n [loading]=\"bookingStatus() === 'loading'\"\n (lookup)=\"lookupBooking()\"\n (brandChanged)=\"onBrandChanged()\"\n />\n </div>\n\n <!-- Hotel: Use dedicated component -->\n @if (product() === 'hotel') {\n <app-sbd-hotel-content\n [bookingData]=\"bookingData()\"\n [bookingStatus]=\"bookingStatus()\"\n [bookingId]=\"bookingId()\"\n [dataVerified]=\"dataVerified()\"\n [bookingNotes]=\"bookingNotes()\"\n [bookingHistory]=\"bookingHistory()\"\n [historyGroups]=\"historyGroups()\"\n [activeContextTab]=\"activeContextTab()\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (addNoteRequested)=\"addNoteDialogOpen.set(true)\"\n (contextTabChanged)=\"activeContextTab.set($event)\"\n />\n }\n\n <!-- Train: read-only booking details -->\n @if (product() === 'train') {\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:60%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:80%\"></div>\n </div>\n }\n @if (bookingStatus() === 'error') {\n <div class=\"sbd-page__error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n <span>Train booking not found. Double-check the ID, then try again.</span>\n </div>\n }\n @if (bookingStatus() === 'success' && bookingData()) {\n <app-sbd-train-details [detail]=\"bookingData()\" [bookingId]=\"bookingId()\" />\n }\n }\n\n <!-- Flight: Keep original 2-column layout -->\n @if (product() === 'flight') {\n <div class=\"sbd-page__content\">\n\n <!-- Left: booking context -->\n <aside class=\"sbd-page__aside\">\n\n <!-- Data search at the top of the sidebar -->\n @if (bookingStatus() === 'success') {\n <app-sbd-data-search [data]=\"bookingData()\" />\n <app-sbd-flight-itinerary-card [product]=\"fcrProduct()\" [data]=\"bookingData()\" [verified]=\"dataVerified()\" />\n }\n\n <!-- Loading skeleton for aside -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:60%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:80%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:50%\"></div>\n </div>\n }\n\n </aside>\n\n <!-- Right: agent workspace -->\n <main class=\"sbd-page__main\">\n\n <!-- Error state -->\n @if (bookingStatus() === 'error') {\n <div class=\"sbd-page__error\">\n <i class=\"pi pi-exclamation-circle\"></i>\n <span>Booking not found. Double-check the ID and product type, then try again.</span>\n </div>\n }\n\n <!-- Content once loaded -->\n @if (bookingStatus() === 'success') {\n\n <!-- Quick actions -->\n <div class=\"sbd-page__quick-actions\">\n <span class=\"sbd-page__quick-label\">Quick actions</span>\n <app-sbd-workflow-actions\n [bookingId]=\"bookingId()\"\n [showReissue]=\"reissueEnabled && product() === 'flight'\"\n (cancelBooking)=\"cancelDialogOpen.set(true)\"\n (reissue)=\"reissueDialogOpen.set(true)\"\n />\n </div>\n\n <!-- Action grid -->\n <app-sbd-action-grid\n [actions]=\"availableActions()\"\n [activeKey]=\"selectedActionKey()\"\n (actionSelected)=\"openAction($event)\"\n />\n\n <!-- Context: Notes + Changes tabbed -->\n <div class=\"sbd-page__context\">\n\n <!-- Tab bar -->\n <div class=\"sbd-page__context-tabs\">\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'changes'\"\n (click)=\"activeContextTab.set('changes')\">\n <i class=\"pi pi-history\"></i>\n Changes\n @if (bookingHistory().length > 0) {\n <span class=\"sbd-page__context-pill sbd-page__context-pill--changes\">{{ bookingHistory().length }}</span>\n }\n </button>\n <button class=\"sbd-page__context-tab\"\n [class.sbd-page__context-tab--active]=\"activeContextTab() === 'notes'\"\n (click)=\"activeContextTab.set('notes')\">\n <i class=\"pi pi-comments\"></i>\n Notes\n <span class=\"sbd-page__context-pill sbd-page__context-pill--notes\">{{ bookingNotes().length }}</span>\n </button>\n @if (activeContextTab() === 'notes') {\n <button pButton label=\"Add Note\" icon=\"pi pi-plus\" class=\"p-button-sm p-button-outlined sbd-page__context-add\"\n (click)=\"addNoteDialogOpen.set(true)\">\n </button>\n }\n </div>\n\n <!-- Notes tab -->\n @if (activeContextTab() === 'notes') {\n <div class=\"sbd-page__notes-list\">\n @if (bookingNotes().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-inbox\"></i> No notes yet\n </div>\n }\n @for (note of bookingNotes(); track $index) {\n <div class=\"sbd-page__note\">\n <div class=\"sbd-page__note-top\">\n <span class=\"sbd-page__note-badge sbd-page__note-badge--{{ note.action | lowercase }}\">\n {{ note.action }}\n </span>\n <span class=\"sbd-page__note-author\">\n {{ note.writtenBy?.firstName }} {{ note.writtenBy?.lastName }}\n </span>\n <span class=\"sbd-page__note-date\">{{ fmtNoteDate(note.date) }}</span>\n </div>\n <p class=\"sbd-page__note-msg\">{{ note.message }}</p>\n </div>\n }\n </div>\n }\n\n <!-- Changes tab -->\n @if (activeContextTab() === 'changes') {\n <div class=\"sbd-page__changes-groups\">\n @if (historyGroups().length === 0) {\n <div class=\"sbd-page__context-empty\">\n <i class=\"pi pi-check\"></i> No changes recorded\n </div>\n }\n @for (group of historyGroups(); track group.changedAt) {\n <div class=\"sbd-page__changes-group\">\n <div class=\"sbd-page__changes-meta\">\n <i class=\"pi pi-user\"></i>\n <strong>{{ group.changedBy }}</strong>\n <span class=\"sbd-page__changes-sep\">\u00B7</span>\n <span>{{ fmtDate(group.changedAt) }}</span>\n </div>\n <table class=\"sbd-page__changes-table\">\n <thead>\n <tr>\n <th>Field</th>\n <th><i class=\"pi pi-times-circle\"></i> Before</th>\n <th><i class=\"pi pi-check-circle\"></i> After</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of group.entries; track entry.field) {\n <tr>\n <td class=\"sbd-page__changes-field\">{{ formatField(entry.field) }}</td>\n <td class=\"sbd-page__changes-old\">{{ entry.from }}</td>\n <td class=\"sbd-page__changes-new\">{{ entry.to }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n }\n\n </div>\n\n }\n\n <!-- Loading skeleton for main -->\n @if (bookingStatus() === 'loading') {\n <div class=\"sbd-page__skeleton-card\">\n <div class=\"sbd-page__skeleton-row\" style=\"width:40%\"></div>\n <div class=\"sbd-page__skeleton-row\" style=\"width:25%\"></div>\n </div>\n }\n\n </main>\n\n </div>\n }\n\n</div>\n\n<app-sbd-cancel-dialog\n [visible]=\"cancelDialogOpen() && product() === 'flight'\"\n [bookingId]=\"bookingId()\"\n [product]=\"fcrProduct()\"\n [pnrInfo]=\"cancelPnrInfo()\"\n [bookingInfo]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (confirmed)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<almatar-hotel-auto-cancellation\n *ngIf=\"cancelDialogOpen() && product() === 'hotel'\"\n [selectedHotelBooking]=\"bookingData()\"\n (closed)=\"cancelDialogOpen.set(false)\"\n (cancellationSuccess)=\"bookingStatus.set('idle'); bookingData.set(null)\"\n/>\n\n<app-sbd-action-dialog\n [visible]=\"actionDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"fcrProduct()\"\n [actions]=\"availableActions()\"\n [initialKey]=\"selectedActionKey()\"\n [preloadedData]=\"bookingData()\"\n (closed)=\"actionDialogOpen.set(false)\"\n/>\n\n<app-sbd-add-note-dialog\n [visible]=\"addNoteDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [product]=\"fcrProduct()\"\n (closed)=\"addNoteDialogOpen.set(false)\"\n/>\n\n<app-sbd-reissue-dialog\n [visible]=\"reissueDialogOpen()\"\n [bookingId]=\"bookingId()\"\n [bookingData]=\"bookingData()\"\n (closed)=\"reissueDialogOpen.set(false)\"\n/>\n", styles: [".sbd-page{display:flex;flex-direction:column;gap:20px;padding:24px;max-width:1400px;min-height:100%}.sbd-page__search{width:100%}.sbd-page__error{display:flex;align-items:center;gap:10px;padding:16px 20px;background:#fef2f2;border:1px solid rgba(239,68,68,.2);border-radius:10px;color:#ef4444;font-size:13.5px;font-weight:500}.sbd-page__error i{font-size:18px}.sbd-page__content{display:grid;grid-template-columns:320px 1fr;gap:20px;align-items:start}@media (max-width: 960px){.sbd-page__content{grid-template-columns:1fr}}.sbd-page__aside{display:flex;flex-direction:column;gap:16px;position:sticky;top:20px}.sbd-page__main{display:flex;flex-direction:column;gap:16px}.sbd-page__quick-actions{display:flex;align-items:center;gap:12px;padding:12px 16px;background:#fff;border:1px solid #e2e8f0;border-radius:10px;flex-wrap:wrap}.sbd-page__quick-label{font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:#94a3b8;flex-shrink:0}.sbd-page__context{background:#fff;border:1px solid #e2e8f0;border-radius:12px;overflow:hidden}.sbd-page__context-tabs{display:flex;align-items:center;border-bottom:1px solid #e2e8f0;background:#f8fafc}.sbd-page__context-add{margin-left:auto;margin-right:10px}.sbd-page__context-tab{display:flex;align-items:center;gap:7px;padding:11px 20px;font-size:13px;font-weight:600;color:#64748b;background:transparent;border:none;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color .15s,border-color .15s}.sbd-page__context-tab .pi{font-size:13px}.sbd-page__context-tab:hover{color:#334155}.sbd-page__context-tab--active{color:#1e2e4a;border-bottom-color:var(--sbd-primary, #3b82f6);background:#fff}.sbd-page__context-pill{font-size:10.5px;font-weight:700;padding:1px 7px;border-radius:10px}.sbd-page__context-pill--notes{background:#e0e7ff;color:#4338ca}.sbd-page__context-pill--changes{background:#fef3c7;color:#b45309}.sbd-page__context-empty{display:flex;align-items:center;gap:8px;padding:24px 20px;font-size:13px;color:#94a3b8}.sbd-page__context-empty .pi{font-size:16px}.sbd-page__notes-list{display:flex;flex-direction:column}.sbd-page__note{padding:12px 16px;border-bottom:1px solid #f1f5f9;display:flex;flex-direction:column;gap:6px}.sbd-page__note:last-child{border-bottom:none}.sbd-page__note-top{display:flex;align-items:center;gap:8px}.sbd-page__note-badge{font-size:10px;font-weight:800;letter-spacing:.05em;padding:2px 8px;border-radius:10px;text-transform:uppercase;flex-shrink:0}.sbd-page__note-badge--general{background:#dbeafe;color:#1d4ed8}.sbd-page__note-badge--remark{background:#fef3c7;color:#b45309}.sbd-page__note-badge--feedback{background:#f3e8ff;color:#7e22ce}.sbd-page__note-author{font-size:12px;font-weight:700;color:#1e2e4a}.sbd-page__note-date{margin-left:auto;font-size:11px;color:#94a3b8;white-space:nowrap}.sbd-page__note-msg{margin:0;font-size:13px;color:#334155;line-height:1.55}.sbd-page__changes-groups{display:flex;flex-direction:column}.sbd-page__changes-group{border-bottom:1px solid #f1f5f9}.sbd-page__changes-group:last-child{border-bottom:none}.sbd-page__changes-meta{display:flex;align-items:center;gap:6px;padding:8px 16px;background:#fafafa;border-bottom:1px solid #f1f5f9;font-size:11.5px;color:#64748b}.sbd-page__changes-meta strong{font-weight:700;color:#334155}.sbd-page__changes-meta .pi{font-size:11px;color:#94a3b8}.sbd-page__changes-sep{color:#cbd5e1}.sbd-page__changes-table{width:100%;border-collapse:collapse;font-size:12.5px}.sbd-page__changes-table thead tr{background:#f8fafc;border-bottom:1px solid #e2e8f0}.sbd-page__changes-table thead th{padding:7px 14px;text-align:left;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:#64748b;white-space:nowrap}.sbd-page__changes-table thead th .pi{margin-right:4px;font-size:10px}.sbd-page__changes-table thead th:nth-child(2){color:#dc2626}.sbd-page__changes-table thead th:nth-child(3){color:#16a34a}.sbd-page__changes-table tbody tr{border-bottom:1px solid #f1f5f9}.sbd-page__changes-table tbody tr:last-child{border-bottom:none}.sbd-page__changes-table tbody tr:hover{background:#f8fafc}.sbd-page__changes-table tbody td{padding:8px 14px;vertical-align:top;word-break:break-word}.sbd-page__changes-field{font-size:12px;font-weight:600;color:#475569;width:28%}.sbd-page__changes-old{color:#dc2626;font-weight:500;text-decoration:line-through;opacity:.8;width:36%}.sbd-page__changes-new{color:#16a34a;font-weight:700;width:36%}.sbd-page__content--loading{opacity:.6;pointer-events:none}.sbd-page__skeleton-card{background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:20px;display:flex;flex-direction:column;gap:12px}.sbd-page__skeleton-row{height:13px;background:#f1f5f9;border-radius:6px;animation:sbd-shimmer 1.4s ease-in-out infinite}.sbd-page__skeleton-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-top:8px}.sbd-page__skeleton-chip{height:56px;background:#f1f5f9;border-radius:8px;animation:sbd-shimmer 1.4s ease-in-out infinite}@keyframes sbd-shimmer{0%,to{opacity:1}50%{opacity:.45}}@keyframes sbd-fadein{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
|
|
8479
8594
|
}], propDecorators: { onKeydown: [{
|
|
8480
8595
|
type: HostListener,
|
|
8481
8596
|
args: ['document:keydown', ['$event']]
|