@rolatech/angular-property 20.3.1-beta.3 → 20.3.2-beta.1

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.
@@ -11,9 +11,9 @@ import { MatTableModule } from '@angular/material/table';
11
11
  import * as i1$1 from '@angular/router';
12
12
  import { RouterLink, RouterModule, ActivatedRoute, Router, RouterLinkActive, RouterOutlet } from '@angular/router';
13
13
  import { ThumbnailComponent, ImagePlaceholderComponent, Skeleton, ToolbarComponent, Loading, ImagePreviewDialogComponent, BaseComponent, AcceptDialogComponent, RejectDialogComponent, RichLabelComponent, AngularComponentsModule, ConfirmationDialogComponent, TabsComponent, TabComponent, EmptyComponent, SearchBar, ContainerComponent, PageCollectionShellComponent, ListComponent, MediaListComponent, MediaListItemComponent, InputComponent, SpinnerComponent } from '@rolatech/angular-components';
14
- import { PropertyService, FeatureService, offerStatusLabel, PropertyOfferService, PropertyOfferCounterService, InvoiceService, FloorplanService, PropertyHighlightsService, TimeZoneService, PropertyOfferStatus as PropertyOfferStatus$1, PaymentService, TitleService, PropertySearchService, DialogService } from '@rolatech/angular-services';
14
+ import { PropertyService, FeatureService, offerStatusLabel, PropertyOfferService, PropertyOfferCounterService, InvoiceService, FloorplanService, PropertyHighlightsService, TimeZoneService, PropertyOfferStatus as PropertyOfferStatus$1, SnackBarService, TitleService, PropertySearchService, DialogService } from '@rolatech/angular-services';
15
15
  import * as i1 from '@angular/common';
16
- import { CommonModule, NgClass, isPlatformBrowser, ViewportScroller, Location, KeyValuePipe } from '@angular/common';
16
+ import { CommonModule, NgClass, isPlatformBrowser, ViewportScroller, KeyValuePipe } from '@angular/common';
17
17
  import { PricePipe, APP_CONFIG, AngularCommonModule, OptionsFormatPipe, DecimalDirective, DurationPipe, SafeUrlPipe } from '@rolatech/angular-common';
18
18
  import * as i2 from '@angular/material/icon';
19
19
  import { MatIconModule, MatIcon } from '@angular/material/icon';
@@ -50,6 +50,7 @@ import { MatProgressSpinnerModule, MatProgressSpinner } from '@angular/material/
50
50
  import { DomSanitizer } from '@angular/platform-browser';
51
51
  import * as i4$1 from '@angular/cdk/text-field';
52
52
  import { TextFieldModule } from '@angular/cdk/text-field';
53
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
53
54
  import * as i2$5 from '@angular/material/expansion';
54
55
  import { MatExpansionModule } from '@angular/material/expansion';
55
56
  import { MatCardModule } from '@angular/material/card';
@@ -127,6 +128,7 @@ var PropertyScope;
127
128
  var PropertyViewingStatus;
128
129
  (function (PropertyViewingStatus) {
129
130
  PropertyViewingStatus["PENDING"] = "Pending";
131
+ PropertyViewingStatus["COUNTERED"] = "Countered";
130
132
  PropertyViewingStatus["APPROVED"] = "Approved";
131
133
  PropertyViewingStatus["REJECTED"] = "Rejected";
132
134
  PropertyViewingStatus["CANCELLED"] = "Cancelled";
@@ -1137,7 +1139,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
1137
1139
  ], template: "<rolatech-toolbar title=\"Offers\">\n <div class=\"flex items-center gap-2\">\n <button mat-icon-button (click)=\"toggleSearch()\">\n <mat-icon>search</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"p-2\">\n <rolatech-search-bar\n [(show)]=\"open\"\n placeholder=\"Search offer by offer id or address\"\n #searchBar\n (search)=\"searchByText($event)\"\n (close)=\"onCloseSearch()\"\n ></rolatech-search-bar>\n</div>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n</rolatech-tabs>\n@if (loading) {\n <div class=\"divide-y divide-(--rt-10-percent-layer) flex flex-col px-3\">\n @for (row of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n }\n </div>\n} @else {\n <div class=\"space-y-2 p-2\">\n @if (offers() && offers().length > 0) {\n @for (item of offers(); track item) {\n <rolatech-property-offer-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [offer]=\"item\"></rolatech-property-offer-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </div>\n}\n<mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n>\n</mat-paginator>\n" }]
1138
1140
  }] });
1139
1141
 
1140
- const MY_FORMATS$8 = {
1142
+ const MY_FORMATS$7 = {
1141
1143
  parse: {
1142
1144
  dateInput: 'YYYY-MM-DD',
1143
1145
  },
@@ -1162,7 +1164,7 @@ class OfferEdit extends BaseComponent {
1162
1164
  useClass: MomentDateAdapter,
1163
1165
  deps: [MAT_DATE_LOCALE],
1164
1166
  },
1165
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$8 },
1167
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
1166
1168
  ], usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"offer().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"offer().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"offer().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"offer().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Offer amount</mat-label>\n <input matInput [(ngModel)]=\"offer().amount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"offer().startDate\"\n (dateInput)=\"offer().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatOptionModule }] });
1167
1169
  }
1168
1170
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferEdit, decorators: [{
@@ -1173,7 +1175,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
1173
1175
  useClass: MomentDateAdapter,
1174
1176
  deps: [MAT_DATE_LOCALE],
1175
1177
  },
1176
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$8 },
1178
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
1177
1179
  ], template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"offer().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"offer().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"offer().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"offer().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Offer amount</mat-label>\n <input matInput [(ngModel)]=\"offer().amount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"offer().startDate\"\n (dateInput)=\"offer().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
1178
1180
  }], propDecorators: { offer: [{ type: i0.Input, args: [{ isSignal: true, alias: "offer", required: true }] }, { type: i0.Output, args: ["offerChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
1179
1181
 
@@ -1335,12 +1337,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
1335
1337
 
1336
1338
  class OfferRentalTermsCard {
1337
1339
  ctx = inject(OfferDetailContext);
1340
+ humanize(value) {
1341
+ if (!value) {
1342
+ return '—';
1343
+ }
1344
+ return value
1345
+ .trim()
1346
+ .replace(/[_-]+/g, ' ')
1347
+ .toLowerCase()
1348
+ .replace(/\b\w/g, (char) => char.toUpperCase());
1349
+ }
1350
+ boolLabel(value) {
1351
+ if (value === null || value === undefined) {
1352
+ return '—';
1353
+ }
1354
+ return value ? 'Yes' : 'No';
1355
+ }
1338
1356
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1339
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate |date: 'mediumDate' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : (rt.smoker ? 'Yes' : 'No') }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furnitureRequirement ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
1357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-rental-terms-card\">\n <div class=\"offer-rental-terms-card__head\">\n <div>\n <span class=\"offer-rental-terms-card__eyebrow\">Submitted terms</span>\n <h3 class=\"offer-rental-terms-card__title\">Original rental offer</h3>\n <p class=\"offer-rental-terms-card__description\">These were the first rental terms attached to the submission.</p>\n </div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"offer-rental-terms-card__grid\">\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Offer amount</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Move-in date</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Payment frequency</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Tenancy length</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.tenancyLengthMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Break clause</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.breakClauseMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Pets</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Smoker</span>\n <strong class=\"offer-rental-terms-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Furniture</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.furnitureRequirement) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field offer-rental-terms-card__field--wide\">\n <span class=\"offer-rental-terms-card__label\">Additional requests</span>\n <strong class=\"offer-rental-terms-card__value offer-rental-terms-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-rental-terms-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-rental-terms-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-rental-terms-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-rental-terms-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-rental-terms-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-rental-terms-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-rental-terms-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-rental-terms-card__field--wide{grid-column:1/-1}.offer-rental-terms-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-rental-terms-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-rental-terms-card__value--multiline{white-space:pre-wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
1340
1358
  }
1341
1359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, decorators: [{
1342
1360
  type: Component,
1343
- args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate |date: 'mediumDate' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : (rt.smoker ? 'Yes' : 'No') }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furnitureRequirement ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n" }]
1361
+ args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-rental-terms-card\">\n <div class=\"offer-rental-terms-card__head\">\n <div>\n <span class=\"offer-rental-terms-card__eyebrow\">Submitted terms</span>\n <h3 class=\"offer-rental-terms-card__title\">Original rental offer</h3>\n <p class=\"offer-rental-terms-card__description\">These were the first rental terms attached to the submission.</p>\n </div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"offer-rental-terms-card__grid\">\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Offer amount</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Move-in date</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Payment frequency</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Tenancy length</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.tenancyLengthMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Break clause</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.breakClauseMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Pets</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Smoker</span>\n <strong class=\"offer-rental-terms-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Furniture</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.furnitureRequirement) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field offer-rental-terms-card__field--wide\">\n <span class=\"offer-rental-terms-card__label\">Additional requests</span>\n <strong class=\"offer-rental-terms-card__value offer-rental-terms-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-rental-terms-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-rental-terms-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-rental-terms-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-rental-terms-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-rental-terms-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-rental-terms-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-rental-terms-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-rental-terms-card__field--wide{grid-column:1/-1}.offer-rental-terms-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-rental-terms-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-rental-terms-card__value--multiline{white-space:pre-wrap}\n"] }]
1344
1362
  }] });
1345
1363
 
1346
1364
  class OfferTenantExpanded {
@@ -1412,11 +1430,11 @@ class OfferTenantCard {
1412
1430
  this.onToggleInput()(this.index());
1413
1431
  }
1414
1432
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1415
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantCard, isStandalone: true, selector: "rolatech-offer-tenant-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-start justify-between gap-3 hover:bg-(--rt-raised-background)\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-sm font-semibold truncate\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"text-xs text-(--rt-text-secondary) truncate\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"flex flex-wrap gap-2 pt-1 text-xs\">\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <!-- <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color)\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span> -->\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: OfferTenantExpanded, selector: "rolatech-offer-tenant-expanded", inputs: ["tenant"] }] });
1433
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantCard, isStandalone: true, selector: "rolatech-offer-tenant-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"offer-tenant-card\">\n <button\n type=\"button\"\n class=\"offer-tenant-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-tenant-card__copy\">\n <div class=\"offer-tenant-card__topline\">Applicant {{ index() + 1 }}</div>\n <div class=\"offer-tenant-card__name\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"offer-tenant-card__contact\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"offer-tenant-card__chips\">\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"offer-tenant-card__arrow\" [class.offer-tenant-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-tenant-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-tenant-card__body\">\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-tenant-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-tenant-card__toggle{display:flex;width:100%;padding:1rem;gap:.9rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-tenant-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-tenant-card__copy{display:flex;flex:1;min-width:0;flex-direction:column;gap:.35rem}.offer-tenant-card__topline{color:var(--rt-brand-color);font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.offer-tenant-card__name{color:var(--rt-text-primary);font-size:1rem;font-weight:700;line-height:1.3}.offer-tenant-card__contact{color:var(--rt-text-secondary);font-size:.82rem;line-height:1.5}.offer-tenant-card__chips{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.2rem}.offer-tenant-card__chip{display:inline-flex;align-items:center;min-height:1.85rem;padding:.28rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}.offer-tenant-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-tenant-card__arrow--expanded .offer-tenant-card__icon{transform:rotate(180deg)}.offer-tenant-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-tenant-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"], dependencies: [{ kind: "component", type: OfferTenantExpanded, selector: "rolatech-offer-tenant-expanded", inputs: ["tenant"] }] });
1416
1434
  }
1417
1435
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, decorators: [{
1418
1436
  type: Component,
1419
- args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], host: { class: 'block' }, template: "<div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-start justify-between gap-3 hover:bg-(--rt-raised-background)\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-sm font-semibold truncate\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"text-xs text-(--rt-text-secondary) truncate\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"flex flex-wrap gap-2 pt-1 text-xs\">\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color)\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <!-- <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color)\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span> -->\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n }\n</div>\n" }]
1437
+ args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], host: { class: 'block' }, template: "<div class=\"offer-tenant-card\">\n <button\n type=\"button\"\n class=\"offer-tenant-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-tenant-card__copy\">\n <div class=\"offer-tenant-card__topline\">Applicant {{ index() + 1 }}</div>\n <div class=\"offer-tenant-card__name\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"offer-tenant-card__contact\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"offer-tenant-card__chips\">\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"offer-tenant-card__arrow\" [class.offer-tenant-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-tenant-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-tenant-card__body\">\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-tenant-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-tenant-card__toggle{display:flex;width:100%;padding:1rem;gap:.9rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-tenant-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-tenant-card__copy{display:flex;flex:1;min-width:0;flex-direction:column;gap:.35rem}.offer-tenant-card__topline{color:var(--rt-brand-color);font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.offer-tenant-card__name{color:var(--rt-text-primary);font-size:1rem;font-weight:700;line-height:1.3}.offer-tenant-card__contact{color:var(--rt-text-secondary);font-size:.82rem;line-height:1.5}.offer-tenant-card__chips{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.2rem}.offer-tenant-card__chip{display:inline-flex;align-items:center;min-height:1.85rem;padding:.28rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}.offer-tenant-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-tenant-card__arrow--expanded .offer-tenant-card__icon{transform:rotate(180deg)}.offer-tenant-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-tenant-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"] }]
1420
1438
  }], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: true }] }], tenant: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenant", required: true }] }], onToggleInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "onToggleInput", required: true }] }] } });
1421
1439
 
1422
1440
  class OfferTenantsAccordion {
@@ -1448,11 +1466,11 @@ class OfferTenantsAccordion {
1448
1466
  this.expandedIndexes.set(new Set());
1449
1467
  }
1450
1468
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
1451
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Tenants</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">o.tenants</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-(--rt-text-secondary)\">No tenants</div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "component", type: OfferTenantCard, selector: "rolatech-offer-tenant-card", inputs: ["index", "expanded", "tenant", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
1469
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"offer-tenants-accordion\">\n <div class=\"offer-tenants-accordion__head\">\n <div class=\"offer-tenants-accordion__copy\">\n <span class=\"offer-tenants-accordion__eyebrow\">Applicants</span>\n <h3 class=\"offer-tenants-accordion__title\">Tenant profiles</h3>\n <p class=\"offer-tenants-accordion__description\">\n Review identity, affordability, and guarantor information for each applicant attached to the offer.\n </p>\n </div>\n\n <div class=\"offer-tenants-accordion__tools\">\n <span class=\"offer-tenants-accordion__count\">{{ ts.length }} applicants</span>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-tenants-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-tenants-accordion__empty\">No applicants have been attached to this offer yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-tenants-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-tenants-accordion__head,.offer-tenants-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-tenants-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-tenants-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-tenants-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-tenants-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-tenants-accordion__tool,.offer-tenants-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-tenants-accordion__tool{cursor:pointer;transition:border-color .2s ease,background .2s ease,color .2s ease}.offer-tenants-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-tenants-accordion__tool:disabled{cursor:not-allowed;opacity:.55}.offer-tenants-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-tenants-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-tenants-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"], dependencies: [{ kind: "component", type: OfferTenantCard, selector: "rolatech-offer-tenant-card", inputs: ["index", "expanded", "tenant", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
1452
1470
  }
1453
1471
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, decorators: [{
1454
1472
  type: Component,
1455
- args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (tenants(); as ts) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Tenants</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">o.tenants</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-(--rt-text-secondary)\">No tenants</div>\n }\n</section>\n}\n" }]
1473
+ args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (tenants(); as ts) {\n<section class=\"offer-tenants-accordion\">\n <div class=\"offer-tenants-accordion__head\">\n <div class=\"offer-tenants-accordion__copy\">\n <span class=\"offer-tenants-accordion__eyebrow\">Applicants</span>\n <h3 class=\"offer-tenants-accordion__title\">Tenant profiles</h3>\n <p class=\"offer-tenants-accordion__description\">\n Review identity, affordability, and guarantor information for each applicant attached to the offer.\n </p>\n </div>\n\n <div class=\"offer-tenants-accordion__tools\">\n <span class=\"offer-tenants-accordion__count\">{{ ts.length }} applicants</span>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-tenants-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-tenants-accordion__empty\">No applicants have been attached to this offer yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-tenants-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-tenants-accordion__head,.offer-tenants-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-tenants-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-tenants-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-tenants-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-tenants-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-tenants-accordion__tool,.offer-tenants-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-tenants-accordion__tool{cursor:pointer;transition:border-color .2s ease,background .2s ease,color .2s ease}.offer-tenants-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-tenants-accordion__tool:disabled{cursor:not-allowed;opacity:.55}.offer-tenants-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-tenants-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-tenants-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"] }]
1456
1474
  }] });
1457
1475
 
1458
1476
  class OfferItemCard {
@@ -1462,21 +1480,31 @@ class OfferItemCard {
1462
1480
  return m?.url ?? null;
1463
1481
  }
1464
1482
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1465
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) overflow-hidden\">\n <div class=\"p-4 flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Property item</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">property snapshot</div>\n </div>\n <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"> propertyId: {{ o.item.propertyId }} </span>\n </div>\n\n <div class=\"p-4 pt-0 space-y-4\">\n <div class=\"flex flex-col lg:flex-row gap-4\">\n <img\n class=\"w-full lg:w-72 h-44 rounded-xl object-cover bg-gray-100\"\n [src]=\"firstMediaUrl(o) ?? '/assets/placeholder.jpg'\"\n [alt]=\"o.item.title\"\n />\n\n <div class=\"flex-1 space-y-2\">\n <div class=\"text-base font-semibold\">{{ o.item.title }}</div>\n\n <div class=\"grid grid-cols-2 gap-3 text-sm pt-2\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ o.item.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Bedrooms</div>\n <div class=\"font-medium\">{{ o.item.bedrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Bathrooms</div>\n <div class=\"font-medium\">{{ o.item.bathrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Receptions</div>\n <div class=\"font-medium\">{{ o.item.receptions }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
1483
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-item-card\">\n <div class=\"offer-item-card__head\">\n <div class=\"offer-item-card__copy\">\n <span class=\"offer-item-card__eyebrow\">Property snapshot</span>\n <h3 class=\"offer-item-card__title\">{{ o.item.title }}</h3>\n </div>\n <span class=\"offer-item-card__property-chip\">Property ID {{ o.item.propertyId }}</span>\n </div>\n\n <div class=\"offer-item-card__content\">\n @if (firstMediaUrl(o); as mediaUrl) {\n <img class=\"offer-item-card__media\" [src]=\"mediaUrl\" [alt]=\"o.item.title\" />\n } @else {\n <div class=\"offer-item-card__placeholder\">Property image unavailable</div>\n }\n\n <div class=\"offer-item-card__panel\">\n <div class=\"offer-item-card__price\">{{ o.item.amount | price }}</div>\n\n <div class=\"offer-item-card__facts\">\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bedrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bedrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bathrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bathrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Receptions</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.receptions }}</strong>\n </article>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-item-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:var(--rt-base-background, #ffffff);box-shadow:0 18px 38px -34px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.offer-item-card__head,.offer-item-card__copy{display:flex;flex-direction:column;gap:.45rem}.offer-item-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.3rem .64rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-item-card__title{margin:0;color:var(--rt-text-primary);font-size:1.2rem;line-height:1.2;font-weight:750}.offer-item-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-item-card__property-chip{display:inline-flex;align-self:flex-start;min-height:2rem;padding:.38rem .7rem;border-radius:999px;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-item-card__content{display:grid;gap:1rem}.offer-item-card__media,.offer-item-card__placeholder{width:100%;min-height:15rem;border-radius:1.15rem}.offer-item-card__media{object-fit:cover;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent)}.offer-item-card__placeholder{display:grid;place-items:center;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 86%,var(--rt-brand-color) 14%));color:var(--rt-text-secondary);text-align:center;padding:1rem}.offer-item-card__panel{display:flex;flex-direction:column;gap:1rem}.offer-item-card__price{color:var(--rt-text-primary);font-size:clamp(1.5rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-item-card__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.85rem}.offer-item-card__fact{display:flex;flex-direction:column;gap:.2rem;padding:.85rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-item-card__fact-label{color:var(--rt-text-secondary);font-size:.76rem}.offer-item-card__fact-value{color:var(--rt-text-primary);font-size:1rem}@media(min-width:960px){.offer-item-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-item-card__content{grid-template-columns:minmax(0,1.1fr) minmax(18rem,.95fr);align-items:start}.offer-item-card__media{min-height:18rem}}\n"], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
1466
1484
  }
1467
1485
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, decorators: [{
1468
1486
  type: Component,
1469
- args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) overflow-hidden\">\n <div class=\"p-4 flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Property item</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">property snapshot</div>\n </div>\n <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"> propertyId: {{ o.item.propertyId }} </span>\n </div>\n\n <div class=\"p-4 pt-0 space-y-4\">\n <div class=\"flex flex-col lg:flex-row gap-4\">\n <img\n class=\"w-full lg:w-72 h-44 rounded-xl object-cover bg-gray-100\"\n [src]=\"firstMediaUrl(o) ?? '/assets/placeholder.jpg'\"\n [alt]=\"o.item.title\"\n />\n\n <div class=\"flex-1 space-y-2\">\n <div class=\"text-base font-semibold\">{{ o.item.title }}</div>\n\n <div class=\"grid grid-cols-2 gap-3 text-sm pt-2\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ o.item.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Bedrooms</div>\n <div class=\"font-medium\">{{ o.item.bedrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Bathrooms</div>\n <div class=\"font-medium\">{{ o.item.bathrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Receptions</div>\n <div class=\"font-medium\">{{ o.item.receptions }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n}\n" }]
1487
+ args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-item-card\">\n <div class=\"offer-item-card__head\">\n <div class=\"offer-item-card__copy\">\n <span class=\"offer-item-card__eyebrow\">Property snapshot</span>\n <h3 class=\"offer-item-card__title\">{{ o.item.title }}</h3>\n </div>\n <span class=\"offer-item-card__property-chip\">Property ID {{ o.item.propertyId }}</span>\n </div>\n\n <div class=\"offer-item-card__content\">\n @if (firstMediaUrl(o); as mediaUrl) {\n <img class=\"offer-item-card__media\" [src]=\"mediaUrl\" [alt]=\"o.item.title\" />\n } @else {\n <div class=\"offer-item-card__placeholder\">Property image unavailable</div>\n }\n\n <div class=\"offer-item-card__panel\">\n <div class=\"offer-item-card__price\">{{ o.item.amount | price }}</div>\n\n <div class=\"offer-item-card__facts\">\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bedrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bedrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bathrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bathrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Receptions</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.receptions }}</strong>\n </article>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-item-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:var(--rt-base-background, #ffffff);box-shadow:0 18px 38px -34px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.offer-item-card__head,.offer-item-card__copy{display:flex;flex-direction:column;gap:.45rem}.offer-item-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.3rem .64rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-item-card__title{margin:0;color:var(--rt-text-primary);font-size:1.2rem;line-height:1.2;font-weight:750}.offer-item-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-item-card__property-chip{display:inline-flex;align-self:flex-start;min-height:2rem;padding:.38rem .7rem;border-radius:999px;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-item-card__content{display:grid;gap:1rem}.offer-item-card__media,.offer-item-card__placeholder{width:100%;min-height:15rem;border-radius:1.15rem}.offer-item-card__media{object-fit:cover;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent)}.offer-item-card__placeholder{display:grid;place-items:center;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 86%,var(--rt-brand-color) 14%));color:var(--rt-text-secondary);text-align:center;padding:1rem}.offer-item-card__panel{display:flex;flex-direction:column;gap:1rem}.offer-item-card__price{color:var(--rt-text-primary);font-size:clamp(1.5rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-item-card__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.85rem}.offer-item-card__fact{display:flex;flex-direction:column;gap:.2rem;padding:.85rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-item-card__fact-label{color:var(--rt-text-secondary);font-size:.76rem}.offer-item-card__fact-value{color:var(--rt-text-primary);font-size:1rem}@media(min-width:960px){.offer-item-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-item-card__content{grid-template-columns:minmax(0,1.1fr) minmax(18rem,.95fr);align-items:start}.offer-item-card__media{min-height:18rem}}\n"] }]
1470
1488
  }] });
1471
1489
 
1472
1490
  class OfferSaleDetailsCard {
1473
1491
  ctx = inject(OfferDetailContext);
1492
+ humanize(value) {
1493
+ if (!value) {
1494
+ return '—';
1495
+ }
1496
+ return value
1497
+ .trim()
1498
+ .replace(/[_-]+/g, ' ')
1499
+ .toLowerCase()
1500
+ .replace(/\b\w/g, (char) => char.toUpperCase());
1501
+ }
1474
1502
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1475
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Sale details</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">o.sale</div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ s.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Payment method</div>\n <div class=\"font-medium\">{{ s.paymentMethod ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Buyer name</div>\n <div class=\"font-medium\">{{ s.buyerName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Phone</div>\n <div class=\"font-medium\">{{ s.phone ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Email</div>\n <div class=\"font-medium\">{{ s.email ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Proposed exchange date</div>\n <div class=\"font-medium\">{{ s.proposedExchangeDate ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">Solicitor company</div>\n <div class=\"font-medium\">{{ s.solicitorCompanyName ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }] });
1503
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-sale-details-card\">\n <div class=\"offer-sale-details-card__head\">\n <div>\n <span class=\"offer-sale-details-card__eyebrow\">Buyer terms</span>\n <h3 class=\"offer-sale-details-card__title\">Sale offer details</h3>\n <p class=\"offer-sale-details-card__description\">Buyer details and funding information attached to the submitted sale offer.</p>\n </div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"offer-sale-details-card__grid\">\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Offer amount</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.amount | price }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Payment method</span>\n <strong class=\"offer-sale-details-card__value\">{{ humanize(s.paymentMethod) }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Buyer name</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.buyerName ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Phone</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.phone ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Email</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.email ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Exchange target</span>\n <strong class=\"offer-sale-details-card__value\">{{\n s.proposedExchangeDate ? (s.proposedExchangeDate | date: 'mediumDate') : '\u2014'\n }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field offer-sale-details-card__field--wide\">\n <span class=\"offer-sale-details-card__label\">Solicitor company</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.solicitorCompanyName ?? '\u2014' }}</strong>\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-sale-details-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-sale-details-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-sale-details-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-sale-details-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-sale-details-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-sale-details-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-sale-details-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-sale-details-card__field--wide{grid-column:1/-1}.offer-sale-details-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-sale-details-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] });
1476
1504
  }
1477
1505
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, decorators: [{
1478
1506
  type: Component,
1479
- args: [{ selector: 'rolatech-offer-sale-details-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Sale details</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">o.sale</div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ s.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Payment method</div>\n <div class=\"font-medium\">{{ s.paymentMethod ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Buyer name</div>\n <div class=\"font-medium\">{{ s.buyerName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Phone</div>\n <div class=\"font-medium\">{{ s.phone ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Email</div>\n <div class=\"font-medium\">{{ s.email ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Proposed exchange date</div>\n <div class=\"font-medium\">{{ s.proposedExchangeDate ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">Solicitor company</div>\n <div class=\"font-medium\">{{ s.solicitorCompanyName ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n" }]
1507
+ args: [{ selector: 'rolatech-offer-sale-details-card', imports: [CommonModule, PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-sale-details-card\">\n <div class=\"offer-sale-details-card__head\">\n <div>\n <span class=\"offer-sale-details-card__eyebrow\">Buyer terms</span>\n <h3 class=\"offer-sale-details-card__title\">Sale offer details</h3>\n <p class=\"offer-sale-details-card__description\">Buyer details and funding information attached to the submitted sale offer.</p>\n </div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"offer-sale-details-card__grid\">\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Offer amount</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.amount | price }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Payment method</span>\n <strong class=\"offer-sale-details-card__value\">{{ humanize(s.paymentMethod) }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Buyer name</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.buyerName ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Phone</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.phone ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Email</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.email ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Exchange target</span>\n <strong class=\"offer-sale-details-card__value\">{{\n s.proposedExchangeDate ? (s.proposedExchangeDate | date: 'mediumDate') : '\u2014'\n }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field offer-sale-details-card__field--wide\">\n <span class=\"offer-sale-details-card__label\">Solicitor company</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.solicitorCompanyName ?? '\u2014' }}</strong>\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-sale-details-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-sale-details-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-sale-details-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-sale-details-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-sale-details-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-sale-details-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-sale-details-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-sale-details-card__field--wide{grid-column:1/-1}.offer-sale-details-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-sale-details-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}\n"] }]
1480
1508
  }] });
1481
1509
 
1482
1510
  // offer-manage.facade.ts
@@ -1878,11 +1906,11 @@ class OfferHistoryCard {
1878
1906
  return `${this.actorLabel(h.actor)} • ${this.actionLabel(h.action)}`;
1879
1907
  }
1880
1908
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1881
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryCard, isStandalone: true, selector: "rolatech-offer-history-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, history: { classPropertyName: "history", publicName: "history", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-center justify-between gap-3 hover:bg-(--rt-raised-background)\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 flex items-center gap-3\">\n <div class=\"text-sm opacity-70\">{{ history().timestamp | date:'EEEE, dd MMM yyyy HH:MM' }}</div>\n <div class=\"text-sm font-medium\">{{ actorLabel(history().actor) }} - {{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <!-- <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color)\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span> -->\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-history-expanded [history]=\"history()\" />\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferHistoryExpanded, selector: "rolatech-offer-history-expanded", inputs: ["history"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }], encapsulation: i0.ViewEncapsulation.None });
1909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryCard, isStandalone: true, selector: "rolatech-offer-history-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, history: { classPropertyName: "history", publicName: "history", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"offer-history-card\">\n <button\n type=\"button\"\n class=\"offer-history-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-history-card__copy\">\n <div class=\"offer-history-card__meta\">\n <span class=\"offer-history-card__actor\">{{ actorLabel(history().actor) }}</span>\n <span class=\"offer-history-card__timestamp\">{{ formatTs(history().timestamp) }}</span>\n </div>\n <div class=\"offer-history-card__summary\">{{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"offer-history-card__arrow\" [class.offer-history-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-history-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-history-card__body\">\n <rolatech-offer-history-expanded [history]=\"history()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-history-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-history-card__toggle{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.9rem;padding:1rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-history-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-history-card__copy{display:flex;min-width:0;flex:1;flex-direction:column;gap:.35rem}.offer-history-card__meta{display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}.offer-history-card__actor{display:inline-flex;align-items:center;min-height:1.8rem;padding:.26rem .58rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700}.offer-history-card__timestamp{color:var(--rt-text-secondary);font-size:.8rem}.offer-history-card__summary{color:var(--rt-text-primary);font-size:.98rem;font-weight:700;line-height:1.35}.offer-history-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-history-card__arrow--expanded .offer-history-card__icon{transform:rotate(180deg)}.offer-history-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-history-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferHistoryExpanded, selector: "rolatech-offer-history-expanded", inputs: ["history"] }], encapsulation: i0.ViewEncapsulation.None });
1882
1910
  }
1883
1911
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, decorators: [{
1884
1912
  type: Component,
1885
- args: [{ selector: 'rolatech-offer-history-card', imports: [CommonModule, OfferHistoryExpanded], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "<div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-center justify-between gap-3 hover:bg-(--rt-raised-background)\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 flex items-center gap-3\">\n <div class=\"text-sm opacity-70\">{{ history().timestamp | date:'EEEE, dd MMM yyyy HH:MM' }}</div>\n <div class=\"text-sm font-medium\">{{ actorLabel(history().actor) }} - {{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <!-- <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color)\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span> -->\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-history-expanded [history]=\"history()\" />\n }\n</div>\n" }]
1913
+ args: [{ selector: 'rolatech-offer-history-card', imports: [CommonModule, OfferHistoryExpanded], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "<div class=\"offer-history-card\">\n <button\n type=\"button\"\n class=\"offer-history-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-history-card__copy\">\n <div class=\"offer-history-card__meta\">\n <span class=\"offer-history-card__actor\">{{ actorLabel(history().actor) }}</span>\n <span class=\"offer-history-card__timestamp\">{{ formatTs(history().timestamp) }}</span>\n </div>\n <div class=\"offer-history-card__summary\">{{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"offer-history-card__arrow\" [class.offer-history-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-history-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-history-card__body\">\n <rolatech-offer-history-expanded [history]=\"history()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-history-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-history-card__toggle{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.9rem;padding:1rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-history-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-history-card__copy{display:flex;min-width:0;flex:1;flex-direction:column;gap:.35rem}.offer-history-card__meta{display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}.offer-history-card__actor{display:inline-flex;align-items:center;min-height:1.8rem;padding:.26rem .58rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700}.offer-history-card__timestamp{color:var(--rt-text-secondary);font-size:.8rem}.offer-history-card__summary{color:var(--rt-text-primary);font-size:.98rem;font-weight:700;line-height:1.35}.offer-history-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-history-card__arrow--expanded .offer-history-card__icon{transform:rotate(180deg)}.offer-history-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-history-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"] }]
1886
1914
  }], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: true }] }], history: [{ type: i0.Input, args: [{ isSignal: true, alias: "history", required: true }] }], onToggleInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "onToggleInput", required: true }] }] } });
1887
1915
 
1888
1916
  class OfferHistoryAccordion {
@@ -1926,14 +1954,14 @@ class OfferHistoryAccordion {
1926
1954
  }
1927
1955
  }
1928
1956
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
1929
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryAccordion, isStandalone: true, selector: "rolatech-offer-history-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (history(); as ts) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Offer history</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Counter offer history</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-(--rt-text-secondary)\">No history</div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "component", type: OfferHistoryCard, selector: "rolatech-offer-history-card", inputs: ["index", "expanded", "history", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
1957
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryAccordion, isStandalone: true, selector: "rolatech-offer-history-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (history(); as ts) {\n<section class=\"offer-history-accordion\">\n <div class=\"offer-history-accordion__head\">\n <div class=\"offer-history-accordion__copy\">\n <span class=\"offer-history-accordion__eyebrow\">Timeline</span>\n <h3 class=\"offer-history-accordion__title\">Offer history</h3>\n <p class=\"offer-history-accordion__description\">\n Every submitted change, view, and response is recorded here so you can audit the full negotiation trail.\n </p>\n </div>\n\n <div class=\"offer-history-accordion__tools\">\n <span class=\"offer-history-accordion__count\">{{ ts.length }} events</span>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-history-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-history-accordion__empty\">No recorded offer events yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-history-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-history-accordion__head,.offer-history-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-history-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-history-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-history-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-history-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-history-accordion__tool,.offer-history-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-history-accordion__tool{cursor:pointer}.offer-history-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-history-accordion__tool:disabled{opacity:.55;cursor:not-allowed}.offer-history-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-history-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-history-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"], dependencies: [{ kind: "component", type: OfferHistoryCard, selector: "rolatech-offer-history-card", inputs: ["index", "expanded", "history", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
1930
1958
  }
1931
1959
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, decorators: [{
1932
1960
  type: Component,
1933
- args: [{ selector: 'rolatech-offer-history-accordion', imports: [OfferHistoryCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (history(); as ts) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Offer history</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Counter offer history</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-base-background)\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-(--rt-text-secondary)\">No history</div>\n }\n</section>\n}\n" }]
1961
+ args: [{ selector: 'rolatech-offer-history-accordion', imports: [OfferHistoryCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (history(); as ts) {\n<section class=\"offer-history-accordion\">\n <div class=\"offer-history-accordion__head\">\n <div class=\"offer-history-accordion__copy\">\n <span class=\"offer-history-accordion__eyebrow\">Timeline</span>\n <h3 class=\"offer-history-accordion__title\">Offer history</h3>\n <p class=\"offer-history-accordion__description\">\n Every submitted change, view, and response is recorded here so you can audit the full negotiation trail.\n </p>\n </div>\n\n <div class=\"offer-history-accordion__tools\">\n <span class=\"offer-history-accordion__count\">{{ ts.length }} events</span>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-history-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-history-accordion__empty\">No recorded offer events yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-history-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-history-accordion__head,.offer-history-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-history-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-history-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-history-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-history-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-history-accordion__tool,.offer-history-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-history-accordion__tool{cursor:pointer}.offer-history-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-history-accordion__tool:disabled{opacity:.55;cursor:not-allowed}.offer-history-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-history-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-history-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"] }]
1934
1962
  }] });
1935
1963
 
1936
- const MY_FORMATS$7 = {
1964
+ const MY_FORMATS$6 = {
1937
1965
  parse: {
1938
1966
  dateInput: 'YYYY-MM-DD',
1939
1967
  },
@@ -2015,7 +2043,7 @@ class OfferCounterDialog {
2015
2043
  useClass: MomentDateAdapter,
2016
2044
  deps: [MAT_DATE_LOCALE],
2017
2045
  },
2018
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
2046
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
2019
2047
  ], ngImport: i0, template: "<div class=\"grid\">\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Offer amount (PCM)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().amount\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Move-in date</mat-label>\n <input matInput [min]=\"minDate\" (focus)=\"dp.open()\" [matDatepicker]=\"dp\" [(ngModel)]=\"term().moveInDate\" readonly />\n <mat-datepicker-toggle matIconSuffix [for]=\"dp\"></mat-datepicker-toggle>\n <mat-datepicker #dp></mat-datepicker>\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Payment frequency</mat-label>\n <mat-select [(ngModel)]=\"term().paymentFrequency\">\n <mat-option value=\"MONTHLY\">Monthly</mat-option>\n <mat-option value=\"QUARTERLY\">Quarterly</mat-option>\n <mat-option value=\"SEMI_ANNUALLY\">Semi-annually</mat-option>\n <mat-option value=\"ANNUALLY\">Annually</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Tenancy length (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().tenancyLengthMonths\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Break clause (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select [(ngModel)]=\"term().furniture\">\n <mat-option value=\"FURNISHED\">Furnished</mat-option>\n <mat-option value=\"UNFURNISHED\">Unfurnished</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\" class=\"w-full\">\n <mat-label>{{ petsInputFocused() ? 'Pets' : 'Add a pet (e.g., Dog, Cat)' }}</mat-label>\n\n <mat-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of (term().pets); track p) {\n <mat-chip-row (removed)=\"removePet(p)\">\n {{ p }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + p\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n\n <input\n matInput\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (focus)=\"petsInputFocused.set(true)\"\n (blur)=\"petsInputFocused.set(false)\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n @if ((term().pets).length <= 0) {\n <mat-error>At least one pet or none is required</mat-error>\n }\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" [(ngModel)]=\"term().smoker\">\n <span class=\"text-lg font-bold\">Smoker</span>\n </mat-slide-toggle>\n\n <mat-form-field class=\"span2\">\n <mat-label>Additional requests</mat-label>\n <textarea matInput rows=\"3\" [(ngModel)]=\"term().additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i5$1.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i7.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i7.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i7.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i7.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
2020
2048
  }
2021
2049
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterDialog, decorators: [{
@@ -2037,7 +2065,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
2037
2065
  useClass: MomentDateAdapter,
2038
2066
  deps: [MAT_DATE_LOCALE],
2039
2067
  },
2040
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
2068
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
2041
2069
  ], template: "<div class=\"grid\">\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Offer amount (PCM)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().amount\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Move-in date</mat-label>\n <input matInput [min]=\"minDate\" (focus)=\"dp.open()\" [matDatepicker]=\"dp\" [(ngModel)]=\"term().moveInDate\" readonly />\n <mat-datepicker-toggle matIconSuffix [for]=\"dp\"></mat-datepicker-toggle>\n <mat-datepicker #dp></mat-datepicker>\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Payment frequency</mat-label>\n <mat-select [(ngModel)]=\"term().paymentFrequency\">\n <mat-option value=\"MONTHLY\">Monthly</mat-option>\n <mat-option value=\"QUARTERLY\">Quarterly</mat-option>\n <mat-option value=\"SEMI_ANNUALLY\">Semi-annually</mat-option>\n <mat-option value=\"ANNUALLY\">Annually</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Tenancy length (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().tenancyLengthMonths\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Break clause (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select [(ngModel)]=\"term().furniture\">\n <mat-option value=\"FURNISHED\">Furnished</mat-option>\n <mat-option value=\"UNFURNISHED\">Unfurnished</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\" class=\"w-full\">\n <mat-label>{{ petsInputFocused() ? 'Pets' : 'Add a pet (e.g., Dog, Cat)' }}</mat-label>\n\n <mat-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of (term().pets); track p) {\n <mat-chip-row (removed)=\"removePet(p)\">\n {{ p }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + p\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n\n <input\n matInput\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (focus)=\"petsInputFocused.set(true)\"\n (blur)=\"petsInputFocused.set(false)\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n @if ((term().pets).length <= 0) {\n <mat-error>At least one pet or none is required</mat-error>\n }\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" [(ngModel)]=\"term().smoker\">\n <span class=\"text-lg font-bold\">Smoker</span>\n </mat-slide-toggle>\n\n <mat-form-field class=\"span2\">\n <mat-label>Additional requests</mat-label>\n <textarea matInput rows=\"3\" [(ngModel)]=\"term().additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n" }]
2042
2070
  }], propDecorators: { term: [{ type: i0.Input, args: [{ isSignal: true, alias: "term", required: true }] }, { type: i0.Output, args: ["termChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
2043
2071
 
@@ -2860,22 +2888,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
2860
2888
 
2861
2889
  class OfferCounterLatestCard {
2862
2890
  f = inject(PropertyOfferNegotiationFacade);
2891
+ humanize(value) {
2892
+ if (!value) {
2893
+ return '—';
2894
+ }
2895
+ return value
2896
+ .trim()
2897
+ .replace(/[_-]+/g, ' ')
2898
+ .toLowerCase()
2899
+ .replace(/\b\w/g, (char) => char.toUpperCase());
2900
+ }
2901
+ boolLabel(value) {
2902
+ if (value === null || value === undefined) {
2903
+ return '—';
2904
+ }
2905
+ return value ? 'Yes' : 'No';
2906
+ }
2863
2907
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
2864
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterLatestCard, isStandalone: true, selector: "rolatech-offer-counter-latest-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.latest(); as rt) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div class=\"flex justify-between items-center\">\n <div class=\"text-sm font-semibold text-(--rt-brand-color) mb-2\">\n <span>Updated Offer</span>\n @if (f.editMode()) {\n <span class=\"ml-2 text-sm\">Editing...</span>\n }\n </div>\n </div>\n @if (f.editMode()) {\n <rolatech-offer-counter-edit />\n } @else {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.moveInDate | date: 'mediumDate' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : rt.smoker ? 'Yes' : 'No' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.furniture ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferCounterEdit, selector: "rolatech-offer-counter-edit" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
2908
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterLatestCard, isStandalone: true, selector: "rolatech-offer-counter-latest-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.latest(); as rt) {\n<section class=\"offer-counter-latest-card\">\n <div class=\"offer-counter-latest-card__head\">\n <div>\n <span class=\"offer-counter-latest-card__eyebrow\">Negotiated terms</span>\n <h3 class=\"offer-counter-latest-card__title\">Latest offer version</h3>\n <p class=\"offer-counter-latest-card__description\">This version reflects the newest negotiated rental terms.</p>\n </div>\n\n <div class=\"offer-counter-latest-card__badges\">\n @if (f.editedFields().size) {\n <span class=\"offer-counter-latest-card__badge\">{{ f.editedFields().size }} updated fields</span>\n } @if (f.editMode()) {\n <span class=\"offer-counter-latest-card__badge offer-counter-latest-card__badge--active\">Editing</span>\n }\n </div>\n </div>\n\n @if (f.editMode()) {\n <div class=\"offer-counter-latest-card__editor\">\n <rolatech-offer-counter-edit />\n </div>\n } @else {\n <div class=\"offer-counter-latest-card__grid\">\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Offer amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field offer-counter-latest-card__field--wide\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value offer-counter-latest-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-counter-latest-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 10%,var(--rt-base-background, #ffffff)),color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent))}.offer-counter-latest-card__head{display:flex;flex-direction:column;gap:.75rem}.offer-counter-latest-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:#3b82f61f;color:#1d4ed8;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-latest-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:760}.offer-counter-latest-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-latest-card__badges{display:flex;flex-wrap:wrap;gap:.5rem}.offer-counter-latest-card__badge,.offer-counter-latest-card__edited{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border-radius:999px;font-size:.76rem;font-weight:700}.offer-counter-latest-card__badge{border:1px solid rgba(59,130,246,.18);background:#3b82f61a;color:#1d4ed8}.offer-counter-latest-card__badge--active{border-color:#10b98133;background:#10b9811f;color:#047857}.offer-counter-latest-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-latest-card__field{display:flex;flex-direction:column;gap:.3rem;padding:.92rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.offer-counter-latest-card__field--wide{grid-column:1/-1}.offer-counter-latest-card__label-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.offer-counter-latest-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-latest-card__edited{border:1px solid rgba(249,115,22,.22);background:#f973161a;color:#c2410c}.offer-counter-latest-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-counter-latest-card__value--multiline{white-space:pre-wrap}.offer-counter-latest-card__editor{padding:.25rem 0 0}@media(min-width:960px){.offer-counter-latest-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferCounterEdit, selector: "rolatech-offer-counter-edit" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
2865
2909
  }
2866
2910
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, decorators: [{
2867
2911
  type: Component,
2868
- args: [{ selector: 'rolatech-offer-counter-latest-card', imports: [CommonModule, PricePipe, OfferCounterEdit, MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.latest(); as rt) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div class=\"flex justify-between items-center\">\n <div class=\"text-sm font-semibold text-(--rt-brand-color) mb-2\">\n <span>Updated Offer</span>\n @if (f.editMode()) {\n <span class=\"ml-2 text-sm\">Editing...</span>\n }\n </div>\n </div>\n @if (f.editMode()) {\n <rolatech-offer-counter-edit />\n } @else {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.moveInDate | date: 'mediumDate' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : rt.smoker ? 'Yes' : 'No' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium\">{{ rt.furniture ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">\n <span>Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"ml-2 text-xs text-orange-500\">Edited</span>\n }\n </div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n" }]
2912
+ args: [{ selector: 'rolatech-offer-counter-latest-card', imports: [CommonModule, PricePipe, OfferCounterEdit, MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.latest(); as rt) {\n<section class=\"offer-counter-latest-card\">\n <div class=\"offer-counter-latest-card__head\">\n <div>\n <span class=\"offer-counter-latest-card__eyebrow\">Negotiated terms</span>\n <h3 class=\"offer-counter-latest-card__title\">Latest offer version</h3>\n <p class=\"offer-counter-latest-card__description\">This version reflects the newest negotiated rental terms.</p>\n </div>\n\n <div class=\"offer-counter-latest-card__badges\">\n @if (f.editedFields().size) {\n <span class=\"offer-counter-latest-card__badge\">{{ f.editedFields().size }} updated fields</span>\n } @if (f.editMode()) {\n <span class=\"offer-counter-latest-card__badge offer-counter-latest-card__badge--active\">Editing</span>\n }\n </div>\n </div>\n\n @if (f.editMode()) {\n <div class=\"offer-counter-latest-card__editor\">\n <rolatech-offer-counter-edit />\n </div>\n } @else {\n <div class=\"offer-counter-latest-card__grid\">\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Offer amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field offer-counter-latest-card__field--wide\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value offer-counter-latest-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-counter-latest-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 10%,var(--rt-base-background, #ffffff)),color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent))}.offer-counter-latest-card__head{display:flex;flex-direction:column;gap:.75rem}.offer-counter-latest-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:#3b82f61f;color:#1d4ed8;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-latest-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:760}.offer-counter-latest-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-latest-card__badges{display:flex;flex-wrap:wrap;gap:.5rem}.offer-counter-latest-card__badge,.offer-counter-latest-card__edited{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border-radius:999px;font-size:.76rem;font-weight:700}.offer-counter-latest-card__badge{border:1px solid rgba(59,130,246,.18);background:#3b82f61a;color:#1d4ed8}.offer-counter-latest-card__badge--active{border-color:#10b98133;background:#10b9811f;color:#047857}.offer-counter-latest-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-latest-card__field{display:flex;flex-direction:column;gap:.3rem;padding:.92rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.offer-counter-latest-card__field--wide{grid-column:1/-1}.offer-counter-latest-card__label-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.offer-counter-latest-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-latest-card__edited{border:1px solid rgba(249,115,22,.22);background:#f973161a;color:#c2410c}.offer-counter-latest-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-counter-latest-card__value--multiline{white-space:pre-wrap}.offer-counter-latest-card__editor{padding:.25rem 0 0}@media(min-width:960px){.offer-counter-latest-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between}}\n"] }]
2869
2913
  }] });
2870
2914
 
2871
2915
  class OfferCounterPreviousCard {
2872
2916
  f = inject(PropertyOfferNegotiationFacade);
2917
+ humanize(value) {
2918
+ if (!value) {
2919
+ return '—';
2920
+ }
2921
+ return value
2922
+ .trim()
2923
+ .replace(/[_-]+/g, ' ')
2924
+ .toLowerCase()
2925
+ .replace(/\b\w/g, (char) => char.toUpperCase());
2926
+ }
2927
+ boolLabel(value) {
2928
+ if (value === null || value === undefined) {
2929
+ return '—';
2930
+ }
2931
+ return value ? 'Yes' : 'No';
2932
+ }
2873
2933
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
2874
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterPreviousCard, isStandalone: true, selector: "rolatech-offer-counter-previous-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.previous(); as rt) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3 opacity-60\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n </div>\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate | date: 'mediumDate' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : rt.smoker ? 'Yes' : 'No' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furniture ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n</section>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
2934
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterPreviousCard, isStandalone: true, selector: "rolatech-offer-counter-previous-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.previous(); as rt) {\n<section class=\"offer-counter-previous-card\">\n <div class=\"offer-counter-previous-card__head\">\n <span class=\"offer-counter-previous-card__eyebrow\">Previous version</span>\n <h3 class=\"offer-counter-previous-card__title\">Earlier rental terms</h3>\n <p class=\"offer-counter-previous-card__description\">\n Use this muted snapshot to compare the previous position against the latest counter.\n </p>\n </div>\n\n <div class=\"offer-counter-previous-card__grid\">\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Offer amount</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Move-in date</span>\n <strong class=\"offer-counter-previous-card__value\"\n >{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong\n >\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Payment frequency</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Tenancy length</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Break clause</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Pets</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Smoker</span>\n <strong class=\"offer-counter-previous-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Furniture</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field offer-counter-previous-card__field--wide\">\n <span class=\"offer-counter-previous-card__label\">Additional requests</span>\n <strong class=\"offer-counter-previous-card__value offer-counter-previous-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-counter-previous-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent);opacity:.82}.offer-counter-previous-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-counter-previous-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-text-secondary) 10%,transparent);color:var(--rt-text-secondary);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-previous-card__title{margin:0;color:var(--rt-text-primary);font-size:1.02rem;font-weight:740}.offer-counter-previous-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-previous-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-previous-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.88rem .94rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,transparent)}.offer-counter-previous-card__field--wide{grid-column:1/-1}.offer-counter-previous-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-previous-card__value{color:var(--rt-text-primary);font-size:.94rem;line-height:1.45}.offer-counter-previous-card__value--multiline{white-space:pre-wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
2875
2935
  }
2876
2936
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, decorators: [{
2877
2937
  type: Component,
2878
- args: [{ selector: 'rolatech-offer-counter-previous-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.previous(); as rt) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3 opacity-60\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n </div>\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate | date: 'mediumDate' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : rt.smoker ? 'Yes' : 'No' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furniture ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-(--rt-text-secondary)\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n</section>\n}\n" }]
2938
+ args: [{ selector: 'rolatech-offer-counter-previous-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.previous(); as rt) {\n<section class=\"offer-counter-previous-card\">\n <div class=\"offer-counter-previous-card__head\">\n <span class=\"offer-counter-previous-card__eyebrow\">Previous version</span>\n <h3 class=\"offer-counter-previous-card__title\">Earlier rental terms</h3>\n <p class=\"offer-counter-previous-card__description\">\n Use this muted snapshot to compare the previous position against the latest counter.\n </p>\n </div>\n\n <div class=\"offer-counter-previous-card__grid\">\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Offer amount</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Move-in date</span>\n <strong class=\"offer-counter-previous-card__value\"\n >{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong\n >\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Payment frequency</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Tenancy length</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Break clause</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Pets</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Smoker</span>\n <strong class=\"offer-counter-previous-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Furniture</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field offer-counter-previous-card__field--wide\">\n <span class=\"offer-counter-previous-card__label\">Additional requests</span>\n <strong class=\"offer-counter-previous-card__value offer-counter-previous-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-counter-previous-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent);opacity:.82}.offer-counter-previous-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-counter-previous-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-text-secondary) 10%,transparent);color:var(--rt-text-secondary);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-previous-card__title{margin:0;color:var(--rt-text-primary);font-size:1.02rem;font-weight:740}.offer-counter-previous-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-previous-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-previous-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.88rem .94rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,transparent)}.offer-counter-previous-card__field--wide{grid-column:1/-1}.offer-counter-previous-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-previous-card__value{color:var(--rt-text-primary);font-size:.94rem;line-height:1.45}.offer-counter-previous-card__value--multiline{white-space:pre-wrap}\n"] }]
2879
2939
  }] });
2880
2940
 
2881
2941
  class OfferNegotiationSection {
@@ -2911,11 +2971,11 @@ class OfferNegotiationSection {
2911
2971
  });
2912
2972
  }
2913
2973
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
2914
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferNegotiationSection, isStandalone: true, selector: "rolatech-offer-negotiation-section", host: { classAttribute: "block" }, ngImport: i0, template: "@if (!isTerminal()) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 bg-(--rt-base-background) space-y-3\">\n <header class=\"flex items-start justify-between gap-3\">\n <div class=\"space-y-1\">\n <div class=\"text-sm font-semibold\">Negotiation</div>\n <div class=\"text-xs opacity-70\">Status: <span class=\"font-medium\">{{ statusLabel() }}</span></div>\n @if (f.lock(); as lk) { @if (lk.locked) {\n <div class=\"text-xs text-amber-600\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n } }\n </div>\n\n @if (f.error(); as err) {\n <div class=\"text-xs text-red-600 max-w-[420px] text-right\">{{ err }}</div>\n }\n </header>\n\n <!-- Primary negotiation actions -->\n <div class=\"flex flex-wrap items-center gap-2\">\n <!-- <button mat-stroked-button [disabled]=\"!f.canEdit() || f.editMode()\" (click)=\"f.enterEdit()\">Edit</button> -->\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n <!-- <button mat-stroked-button [disabled]=\"!f.editMode()\" (click)=\"f.cancelEdit()\">Cancel edit</button> -->\n\n <!-- <button mat-raised-button [disabled]=\"!f.canCounter() || !f.editMode() || f.sending()\" (click)=\"f.sendCounter()\">\n @if (f.sending()) { Sending\u2026 } @else { Send counter }\n </button> -->\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) { Working\u2026 } @else { Withdraw }\n </button>\n }\n\n <span class=\"flex-1\"></span>\n\n <!-- Accept/Reject -->\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n\n <!-- Cancel (terminal) -->\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) { Working\u2026 } @else { Cancel }\n </button>\n </div>\n <!-- Lifecycle actions (agent/admin) -->\n @if (f.showLifecycleActions()) {\n <div class=\"pt-3 border-t border-(--rt-border-color)\">\n <div class=\"text-xs font-semibold opacity-70 mb-2\">Next steps</div>\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n\n} @else {\n<!-- compact terminal summary -->\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 bg-(--rt-base-background) space-y-2\">\n <div class=\"text-sm font-semibold\">Negotiation</div>\n <!-- <div class=\"text-xs opacity-70\">Final status: <span class=\"font-medium\">{{ statusLabel() }}</span></div> -->\n <!-- <div class=\"text-xs opacity-70\">\n Final status:\n <span class=\"font-medium\">{{ statusLabel() }}</span>\n @if (f.offer()?.lastActor; as actor) { \u00B7 {{ actor | actorLabel }} }\n </div>\n\n @if (f.offer()?.rejectionNotes; as notes) {\n <div class=\"text-xs whitespace-pre-wrap\">{{ notes }}</div>\n } -->\n\n <!-- optional: view history -->\n <!-- <button mat-stroked-button (click)=\"f.openHistory()\">View history</button> -->\n</section>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], encapsulation: i0.ViewEncapsulation.None });
2974
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferNegotiationSection, isStandalone: true, selector: "rolatech-offer-negotiation-section", host: { classAttribute: "block" }, ngImport: i0, template: "@if (!isTerminal()) {\n<section class=\"offer-negotiation-section\">\n <header class=\"offer-negotiation-section__header\">\n <div class=\"offer-negotiation-section__copy\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation controls</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">Respond to the latest offer</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n Keep the negotiation moving with one clear next action. Every response stays recorded in the offer history below.\n </p>\n </div>\n\n @if (f.lock(); as lk) {\n @if (lk.locked) {\n <div class=\"offer-negotiation-section__lock\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n }\n }\n </header>\n\n @if (f.error(); as err) {\n <div class=\"offer-negotiation-section__alert\">{{ err }}</div>\n }\n\n @if (f.editMode()) {\n <div class=\"offer-negotiation-section__edit-note\">\n Counter editing is active below. Review the updated terms, then send the counter-offer when ready.\n </div>\n }\n\n <div class=\"offer-negotiation-section__actions\">\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) {\n Working...\n } @else {\n {{ f.withdrawLabel() }}\n }\n </button>\n }\n\n <span class=\"offer-negotiation-section__spacer\"></span>\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) {\n Working...\n } @else {\n Cancel\n }\n </button>\n </div>\n\n @if (f.showLifecycleActions()) {\n <div class=\"offer-negotiation-section__secondary\">\n <div class=\"offer-negotiation-section__secondary-copy\">\n <span class=\"offer-negotiation-section__secondary-eyebrow\">Workflow steps</span>\n <h4 class=\"offer-negotiation-section__secondary-title\">Progress the deal after negotiation</h4>\n </div>\n\n <div class=\"offer-negotiation-section__secondary-actions\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n} @else {\n <section class=\"offer-negotiation-section offer-negotiation-section--terminal\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation closed</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">This offer is no longer awaiting action</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n The final state has been recorded. Use the history section below to review how the negotiation reached this outcome.\n </p>\n </section>\n}\n", styles: [":host{display:block}.offer-negotiation-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%);box-shadow:0 18px 42px -38px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.offer-negotiation-section__header,.offer-negotiation-section__copy,.offer-negotiation-section__secondary-copy{display:flex;flex-direction:column;gap:.45rem}.offer-negotiation-section__eyebrow,.offer-negotiation-section__secondary-eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .68rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-negotiation-section__heading{display:flex;flex-direction:column;gap:.75rem}.offer-negotiation-section__title,.offer-negotiation-section__secondary-title{margin:0;color:var(--rt-text-primary);font-size:1.08rem;line-height:1.2;font-weight:700}.offer-negotiation-section__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-negotiation-section__status,.offer-negotiation-section__lock{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.1rem;padding:.38rem .75rem;border-radius:999px;font-size:.8rem;font-weight:700}.offer-negotiation-section__status{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-raised-background, #ffffff);color:var(--rt-text-primary)}.offer-negotiation-section__lock{border:1px solid rgba(245,158,11,.24);background:#f59e0b1f;color:#a16207}.offer-negotiation-section__alert,.offer-negotiation-section__edit-note{padding:.85rem .95rem;border-radius:1rem;font-size:.9rem;line-height:1.55}.offer-negotiation-section__alert{border:1px solid rgba(239,68,68,.2);background:#fef2f2e6;color:#b91c1c}.offer-negotiation-section__edit-note{border:1px solid rgba(59,130,246,.16);background:#eff6ffe6;color:#1d4ed8}.offer-negotiation-section__actions,.offer-negotiation-section__secondary-actions{display:flex;flex-wrap:wrap;gap:.7rem}.offer-negotiation-section__spacer{flex:1 1 auto}.offer-negotiation-section__secondary{display:flex;flex-direction:column;gap:.85rem;padding-top:1rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-negotiation-section--terminal{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent)}@media(min-width:900px){.offer-negotiation-section__header{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-negotiation-section__heading{flex-direction:row;align-items:center;justify-content:space-between}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], encapsulation: i0.ViewEncapsulation.None });
2915
2975
  }
2916
2976
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, decorators: [{
2917
2977
  type: Component,
2918
- args: [{ selector: 'rolatech-offer-negotiation-section', imports: [MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (!isTerminal()) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 bg-(--rt-base-background) space-y-3\">\n <header class=\"flex items-start justify-between gap-3\">\n <div class=\"space-y-1\">\n <div class=\"text-sm font-semibold\">Negotiation</div>\n <div class=\"text-xs opacity-70\">Status: <span class=\"font-medium\">{{ statusLabel() }}</span></div>\n @if (f.lock(); as lk) { @if (lk.locked) {\n <div class=\"text-xs text-amber-600\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n } }\n </div>\n\n @if (f.error(); as err) {\n <div class=\"text-xs text-red-600 max-w-[420px] text-right\">{{ err }}</div>\n }\n </header>\n\n <!-- Primary negotiation actions -->\n <div class=\"flex flex-wrap items-center gap-2\">\n <!-- <button mat-stroked-button [disabled]=\"!f.canEdit() || f.editMode()\" (click)=\"f.enterEdit()\">Edit</button> -->\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n <!-- <button mat-stroked-button [disabled]=\"!f.editMode()\" (click)=\"f.cancelEdit()\">Cancel edit</button> -->\n\n <!-- <button mat-raised-button [disabled]=\"!f.canCounter() || !f.editMode() || f.sending()\" (click)=\"f.sendCounter()\">\n @if (f.sending()) { Sending\u2026 } @else { Send counter }\n </button> -->\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) { Working\u2026 } @else { Withdraw }\n </button>\n }\n\n <span class=\"flex-1\"></span>\n\n <!-- Accept/Reject -->\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n\n <!-- Cancel (terminal) -->\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) { Working\u2026 } @else { Cancel }\n </button>\n </div>\n <!-- Lifecycle actions (agent/admin) -->\n @if (f.showLifecycleActions()) {\n <div class=\"pt-3 border-t border-(--rt-border-color)\">\n <div class=\"text-xs font-semibold opacity-70 mb-2\">Next steps</div>\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n\n} @else {\n<!-- compact terminal summary -->\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 bg-(--rt-base-background) space-y-2\">\n <div class=\"text-sm font-semibold\">Negotiation</div>\n <!-- <div class=\"text-xs opacity-70\">Final status: <span class=\"font-medium\">{{ statusLabel() }}</span></div> -->\n <!-- <div class=\"text-xs opacity-70\">\n Final status:\n <span class=\"font-medium\">{{ statusLabel() }}</span>\n @if (f.offer()?.lastActor; as actor) { \u00B7 {{ actor | actorLabel }} }\n </div>\n\n @if (f.offer()?.rejectionNotes; as notes) {\n <div class=\"text-xs whitespace-pre-wrap\">{{ notes }}</div>\n } -->\n\n <!-- optional: view history -->\n <!-- <button mat-stroked-button (click)=\"f.openHistory()\">View history</button> -->\n</section>\n}\n" }]
2978
+ args: [{ selector: 'rolatech-offer-negotiation-section', imports: [MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (!isTerminal()) {\n<section class=\"offer-negotiation-section\">\n <header class=\"offer-negotiation-section__header\">\n <div class=\"offer-negotiation-section__copy\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation controls</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">Respond to the latest offer</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n Keep the negotiation moving with one clear next action. Every response stays recorded in the offer history below.\n </p>\n </div>\n\n @if (f.lock(); as lk) {\n @if (lk.locked) {\n <div class=\"offer-negotiation-section__lock\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n }\n }\n </header>\n\n @if (f.error(); as err) {\n <div class=\"offer-negotiation-section__alert\">{{ err }}</div>\n }\n\n @if (f.editMode()) {\n <div class=\"offer-negotiation-section__edit-note\">\n Counter editing is active below. Review the updated terms, then send the counter-offer when ready.\n </div>\n }\n\n <div class=\"offer-negotiation-section__actions\">\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) {\n Working...\n } @else {\n {{ f.withdrawLabel() }}\n }\n </button>\n }\n\n <span class=\"offer-negotiation-section__spacer\"></span>\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) {\n Working...\n } @else {\n Cancel\n }\n </button>\n </div>\n\n @if (f.showLifecycleActions()) {\n <div class=\"offer-negotiation-section__secondary\">\n <div class=\"offer-negotiation-section__secondary-copy\">\n <span class=\"offer-negotiation-section__secondary-eyebrow\">Workflow steps</span>\n <h4 class=\"offer-negotiation-section__secondary-title\">Progress the deal after negotiation</h4>\n </div>\n\n <div class=\"offer-negotiation-section__secondary-actions\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n} @else {\n <section class=\"offer-negotiation-section offer-negotiation-section--terminal\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation closed</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">This offer is no longer awaiting action</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n The final state has been recorded. Use the history section below to review how the negotiation reached this outcome.\n </p>\n </section>\n}\n", styles: [":host{display:block}.offer-negotiation-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%);box-shadow:0 18px 42px -38px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.offer-negotiation-section__header,.offer-negotiation-section__copy,.offer-negotiation-section__secondary-copy{display:flex;flex-direction:column;gap:.45rem}.offer-negotiation-section__eyebrow,.offer-negotiation-section__secondary-eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .68rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-negotiation-section__heading{display:flex;flex-direction:column;gap:.75rem}.offer-negotiation-section__title,.offer-negotiation-section__secondary-title{margin:0;color:var(--rt-text-primary);font-size:1.08rem;line-height:1.2;font-weight:700}.offer-negotiation-section__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-negotiation-section__status,.offer-negotiation-section__lock{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.1rem;padding:.38rem .75rem;border-radius:999px;font-size:.8rem;font-weight:700}.offer-negotiation-section__status{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-raised-background, #ffffff);color:var(--rt-text-primary)}.offer-negotiation-section__lock{border:1px solid rgba(245,158,11,.24);background:#f59e0b1f;color:#a16207}.offer-negotiation-section__alert,.offer-negotiation-section__edit-note{padding:.85rem .95rem;border-radius:1rem;font-size:.9rem;line-height:1.55}.offer-negotiation-section__alert{border:1px solid rgba(239,68,68,.2);background:#fef2f2e6;color:#b91c1c}.offer-negotiation-section__edit-note{border:1px solid rgba(59,130,246,.16);background:#eff6ffe6;color:#1d4ed8}.offer-negotiation-section__actions,.offer-negotiation-section__secondary-actions{display:flex;flex-wrap:wrap;gap:.7rem}.offer-negotiation-section__spacer{flex:1 1 auto}.offer-negotiation-section__secondary{display:flex;flex-direction:column;gap:.85rem;padding-top:1rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-negotiation-section--terminal{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent)}@media(min-width:900px){.offer-negotiation-section__header{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-negotiation-section__heading{flex-direction:row;align-items:center;justify-content:space-between}}\n"] }]
2919
2979
  }] });
2920
2980
 
2921
2981
  class PropertyManageOfferDetailComponent extends BaseComponent {
@@ -3277,7 +3337,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
3277
3337
  }]
3278
3338
  }] });
3279
3339
 
3280
- const MY_FORMATS$6 = {
3340
+ const MY_FORMATS$5 = {
3281
3341
  parse: {
3282
3342
  dateInput: 'YYYY-MM-DD',
3283
3343
  },
@@ -3393,7 +3453,7 @@ class PropertyFilterBar {
3393
3453
  useClass: MomentDateAdapter,
3394
3454
  deps: [MAT_DATE_LOCALE],
3395
3455
  },
3396
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
3456
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
3397
3457
  ], ngImport: i0, template: "<div class=\"px-2 mb-3 close\" [class.open]=\"show()\">\n <div class=\"flex flex-col h-min justify-center items-center gap-3\">\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Market</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Market\" [(ngModel)]=\"options().market\">\n @for (m of market; track m) {\n <mat-option [value]=\"m\"> {{ m }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Type</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Type\" [(ngModel)]=\"options().type\">\n @for (type of types | keyvalue; track type) {\n <mat-option [value]=\"type.key\"> {{ type.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Town</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"options().town\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\"> {{ town }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Available date</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\" class=\"z-[1000]\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"options().availableFrom\"\n (dateInput)=\"options().availableFrom = $event.value.format('YYYY-MM-DD')\"\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker panelClass=\"datepicker-on-top\" #startDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/3 overflow-hidden\">\n <div class=\"py-1\">Price range (\u00A3)</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().minPrice\">\n @for (opt of minOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().maxPrice\">\n @for (opt of maxOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/3\">\n <div class=\"py-1\">No. of bedrooms</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min</mat-label>\n <mat-select [(ngModel)]=\"options().minBedrooms\">\n @for (opt of minBedroom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max</mat-label>\n <mat-select [(ngModel)]=\"options().maxBedrooms\">\n @for (opt of maxBedRoom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"h-14 md:w-1/3 self-end w-full\">\n <button mat-flat-button (click)=\"onSearch()\" class=\"w-full min-h-14\">\n <a i18n>Search</a>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}.close{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.open{max-height:1000px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }], encapsulation: i0.ViewEncapsulation.None });
3398
3458
  }
3399
3459
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyFilterBar, decorators: [{
@@ -3413,7 +3473,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
3413
3473
  useClass: MomentDateAdapter,
3414
3474
  deps: [MAT_DATE_LOCALE],
3415
3475
  },
3416
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
3476
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
3417
3477
  ], encapsulation: ViewEncapsulation.None, template: "<div class=\"px-2 mb-3 close\" [class.open]=\"show()\">\n <div class=\"flex flex-col h-min justify-center items-center gap-3\">\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Market</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Market\" [(ngModel)]=\"options().market\">\n @for (m of market; track m) {\n <mat-option [value]=\"m\"> {{ m }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Type</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Type\" [(ngModel)]=\"options().type\">\n @for (type of types | keyvalue; track type) {\n <mat-option [value]=\"type.key\"> {{ type.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Town</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"options().town\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\"> {{ town }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Available date</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\" class=\"z-[1000]\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"options().availableFrom\"\n (dateInput)=\"options().availableFrom = $event.value.format('YYYY-MM-DD')\"\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker panelClass=\"datepicker-on-top\" #startDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/3 overflow-hidden\">\n <div class=\"py-1\">Price range (\u00A3)</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().minPrice\">\n @for (opt of minOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().maxPrice\">\n @for (opt of maxOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/3\">\n <div class=\"py-1\">No. of bedrooms</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min</mat-label>\n <mat-select [(ngModel)]=\"options().minBedrooms\">\n @for (opt of minBedroom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max</mat-label>\n <mat-select [(ngModel)]=\"options().maxBedrooms\">\n @for (opt of maxBedRoom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"h-14 md:w-1/3 self-end w-full\">\n <button mat-flat-button (click)=\"onSearch()\" class=\"w-full min-h-14\">\n <a i18n>Search</a>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}.close{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.open{max-height:1000px}\n"] }]
3418
3478
  }], ctorParameters: () => [], propDecorators: { search: [{ type: i0.Output, args: ["search"] }], reset: [{ type: i0.Output, args: ["reset"] }], show: [{ type: i0.Input, args: [{ isSignal: true, alias: "show", required: false }] }, { type: i0.Output, args: ["showChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }, { type: i0.Output, args: ["optionsChange"] }] } });
3419
3479
 
@@ -4086,7 +4146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4086
4146
  ], template: "<rolatech-container>\n <div class=\"flex flex-col w-full\">\n @if (loading()) {\n <rolatech-property-media-skeleton></rolatech-property-media-skeleton>\n\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <rolatech-property-info-skeleton class=\"w-full md:w-2/3\"></rolatech-property-info-skeleton>\n <rolatech-property-agent-skeleton class=\"w-full md:w-1/3\"></rolatech-property-agent-skeleton>\n </div>\n } @else {\n @if (property(); as property) {\n <rolatech-property-media [media]=\"property.media ? property.media : []\"></rolatech-property-media>\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <div class=\"w-full md:w-2/3\">\n <rolatech-property-info [property]=\"property\" (wish)=\"onWish($event)\" [inWishList]=\"inWishList\">\n </rolatech-property-info>\n <!-- <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer> -->\n @if (features.length > 0) {\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n }\n @if (highlights.length > 0) {\n <rolatech-property-highlights [highlights]=\"highlights\"></rolatech-property-highlights>\n }\n @if (floorplans.length > 0) {\n <rolatech-property-floorplan [floorplans]=\"floorplans\"></rolatech-property-floorplan>\n }\n @if (property.location) {\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n }\n @if (videos() && videos().length > 0) {\n @for (item of videos(); track $index) {\n <rolatech-property-video [video]=\"item\"></rolatech-property-video>\n }\n }\n @if (epc && epc.currentScore) {\n <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc>\n }\n <div class=\"flex flex-col\">\n <div class=\"text-2xl font-bold pt-3\" i18n>Sections</div>\n @for (section of property.sections; track $index) {\n <rolatech-property-section [section]=\"section\"></rolatech-property-section>\n }\n <!-- <rolatech-comments [itemId]=\"property.id\"></rolatech-comments> -->\n </div>\n </div>\n <div class=\"w-full md:w-1/3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-property-actions\n [property]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-property-action-contact [email]=\"user.email\" [phone]=\"user.phone\"></rolatech-property-action-contact>\n }\n </rolatech-property-actions>\n </div>\n </div>\n }\n }\n </div>\n</rolatech-container>\n" }]
4087
4147
  }], ctorParameters: () => [] });
4088
4148
 
4089
- const MY_FORMATS$5 = {
4149
+ const MY_FORMATS$4 = {
4090
4150
  parse: {
4091
4151
  dateInput: 'YYYY-MM-DD',
4092
4152
  },
@@ -4101,7 +4161,6 @@ class PropertyViewingTimeComponent {
4101
4161
  minDate = new Date();
4102
4162
  maxDate = new Date();
4103
4163
  output = output();
4104
- select = output();
4105
4164
  proposedTime = input({
4106
4165
  date: '',
4107
4166
  time: '',
@@ -4111,18 +4170,56 @@ class PropertyViewingTimeComponent {
4111
4170
  this.minDate.setDate(this.minDate.getDate() + 1);
4112
4171
  this.maxDate.setDate(this.maxDate.getDate() + 7);
4113
4172
  }
4114
- ngDoCheck() {
4115
- this.output.emit(this.proposedTime());
4173
+ onDateInput(event) {
4174
+ this.updateSlot({
4175
+ date: this.formatDateValue(event.value),
4176
+ });
4177
+ }
4178
+ onTimeChange(time) {
4179
+ this.updateSlot({
4180
+ time: time ?? '',
4181
+ });
4182
+ }
4183
+ updateSlot(changes) {
4184
+ const current = this.proposedTime() ?? {
4185
+ date: '',
4186
+ time: '',
4187
+ };
4188
+ const next = {
4189
+ ...current,
4190
+ ...changes,
4191
+ };
4192
+ if (current.date === next.date && current.time === next.time) {
4193
+ return;
4194
+ }
4195
+ current.date = next.date;
4196
+ current.time = next.time;
4197
+ this.output.emit(next);
4198
+ }
4199
+ formatDateValue(value) {
4200
+ if (!value) {
4201
+ return '';
4202
+ }
4203
+ if (typeof value === 'object' && value !== null && 'format' in value && typeof value.format === 'function') {
4204
+ return value.format('YYYY-MM-DD');
4205
+ }
4206
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
4207
+ const year = value.getFullYear();
4208
+ const month = String(value.getMonth() + 1).padStart(2, '0');
4209
+ const day = String(value.getDate()).padStart(2, '0');
4210
+ return `${year}-${month}-${day}`;
4211
+ }
4212
+ return String(value);
4116
4213
  }
4117
4214
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4118
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingTimeComponent, isStandalone: true, selector: "rolatech-property-viewing-time", inputs: { proposedTime: { classPropertyName: "proposedTime", publicName: "proposedTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { output: "output", select: "select" }, providers: [
4215
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingTimeComponent, isStandalone: true, selector: "rolatech-property-viewing-time", inputs: { proposedTime: { classPropertyName: "proposedTime", publicName: "proposedTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { output: "output" }, providers: [
4119
4216
  {
4120
4217
  provide: DateAdapter,
4121
4218
  useClass: MomentDateAdapter,
4122
4219
  deps: [MAT_DATE_LOCALE],
4123
4220
  },
4124
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
4125
- ], ngImport: i0, template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [(ngModel)]=\"proposedTime()!.date\"\n (dateInput)=\"proposedTime()!.date = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [(ngModel)]=\"proposedTime()!.time\" required readonly>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }] });
4221
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
4222
+ ], ngImport: i0, template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [ngModel]=\"proposedTime().date || null\"\n (dateInput)=\"onDateInput($event)\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [ngModel]=\"proposedTime().time || null\" (ngModelChange)=\"onTimeChange($event)\" required>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }] });
4126
4223
  }
4127
4224
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, decorators: [{
4128
4225
  type: Component,
@@ -4141,9 +4238,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4141
4238
  useClass: MomentDateAdapter,
4142
4239
  deps: [MAT_DATE_LOCALE],
4143
4240
  },
4144
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
4145
- ], template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [(ngModel)]=\"proposedTime()!.date\"\n (dateInput)=\"proposedTime()!.date = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [(ngModel)]=\"proposedTime()!.time\" required readonly>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
4146
- }], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], select: [{ type: i0.Output, args: ["select"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
4241
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
4242
+ ], template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [ngModel]=\"proposedTime().date || null\"\n (dateInput)=\"onDateInput($event)\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [ngModel]=\"proposedTime().time || null\" (ngModelChange)=\"onTimeChange($event)\" required>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
4243
+ }], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
4147
4244
 
4148
4245
  class PropertyIntentShell {
4149
4246
  property = input(null, ...(ngDevMode ? [{ debugName: "property" }] : []));
@@ -4266,7 +4363,7 @@ class PropertyViewingRequestComponent extends BaseComponent {
4266
4363
  this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
4267
4364
  }
4268
4365
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4269
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", usesInheritance: true, ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Buyer workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n\n <div class=\"property-viewing-request__slots\">\n <div class=\"mb-3\">\n <div class=\"text-lg font-bold\" i18n>Viewing date</div>\n <div class=\"opacity-70\" i18n>\n Please choose 3 different times on at least 2 different days that would work for you.\n </div>\n </div>\n\n @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n </div>\n</rolatech-property-intent-shell>\n", styles: ["mat-form-field{width:100%}.property-viewing-request__header,.property-viewing-request__breadcrumb{margin-bottom:1rem}.property-viewing-request__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-request__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-viewing-request__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-request__form{display:flex;flex-direction:column;gap:.75rem}.property-viewing-request__grid{display:grid;gap:.75rem}.property-viewing-request__slots{margin-top:.25rem}@media(min-width:768px){.property-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output", "select"] }, { kind: "component", type: PropertyIntentShell, selector: "rolatech-property-intent-shell", inputs: ["property"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
4366
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", usesInheritance: true, ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Market workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n\n <div class=\"property-viewing-request__slots\">\n <div class=\"mb-3\">\n <div class=\"text-lg font-bold\" i18n>Viewing date</div>\n <div class=\"opacity-70\" i18n>\n Please choose 3 different times on at least 2 different days that would work for you.\n </div>\n </div>\n\n @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n </div>\n</rolatech-property-intent-shell>\n", styles: ["mat-form-field{width:100%}.property-viewing-request__header,.property-viewing-request__breadcrumb{margin-bottom:1rem}.property-viewing-request__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-request__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-viewing-request__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-request__form{display:flex;flex-direction:column;gap:.75rem}.property-viewing-request__grid{display:grid;gap:.75rem}.property-viewing-request__slots{margin-top:.25rem}@media(min-width:768px){.property-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output"] }, { kind: "component", type: PropertyIntentShell, selector: "rolatech-property-intent-shell", inputs: ["property"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
4270
4367
  }
4271
4368
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, decorators: [{
4272
4369
  type: Component,
@@ -4284,7 +4381,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4284
4381
  PropertyViewingTimeComponent,
4285
4382
  PropertyIntentShell,
4286
4383
  PropertyMarketBreadcrumbComponent,
4287
- ], template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Buyer workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n\n <div class=\"property-viewing-request__slots\">\n <div class=\"mb-3\">\n <div class=\"text-lg font-bold\" i18n>Viewing date</div>\n <div class=\"opacity-70\" i18n>\n Please choose 3 different times on at least 2 different days that would work for you.\n </div>\n </div>\n\n @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n </div>\n</rolatech-property-intent-shell>\n", styles: ["mat-form-field{width:100%}.property-viewing-request__header,.property-viewing-request__breadcrumb{margin-bottom:1rem}.property-viewing-request__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-request__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-viewing-request__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-request__form{display:flex;flex-direction:column;gap:.75rem}.property-viewing-request__grid{display:grid;gap:.75rem}.property-viewing-request__slots{margin-top:.25rem}@media(min-width:768px){.property-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}\n"] }]
4384
+ ], template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Market workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n\n <div class=\"property-viewing-request__slots\">\n <div class=\"mb-3\">\n <div class=\"text-lg font-bold\" i18n>Viewing date</div>\n <div class=\"opacity-70\" i18n>\n Please choose 3 different times on at least 2 different days that would work for you.\n </div>\n </div>\n\n @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n </div>\n</rolatech-property-intent-shell>\n", styles: ["mat-form-field{width:100%}.property-viewing-request__header,.property-viewing-request__breadcrumb{margin-bottom:1rem}.property-viewing-request__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-request__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-viewing-request__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-request__form{display:flex;flex-direction:column;gap:.75rem}.property-viewing-request__grid{display:grid;gap:.75rem}.property-viewing-request__slots{margin-top:.25rem}@media(min-width:768px){.property-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}\n"] }]
4288
4385
  }], ctorParameters: () => [] });
4289
4386
 
4290
4387
  class PropertyOfferIndexComponent extends BaseComponent {
@@ -4533,6 +4630,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4533
4630
 
4534
4631
  class OfferHeaderCard {
4535
4632
  ctx = inject(OfferDetailContext);
4633
+ offerStatusLabel = offerStatusLabel;
4536
4634
  ok = new Set([
4537
4635
  PropertyOfferStatus$1.ACCEPTED,
4538
4636
  PropertyOfferStatus$1.REFERENCES_PASSED,
@@ -4567,15 +4665,15 @@ class OfferHeaderCard {
4567
4665
  ]);
4568
4666
  statusBadgeClass(status) {
4569
4667
  if (!status)
4570
- return 'bg-gray-50 text-gray-700 border-gray-200';
4668
+ return 'offer-header-card__status offer-header-card__status--neutral';
4571
4669
  if (this.ok.has(status))
4572
- return 'bg-green-50 text-green-700 border-green-200';
4670
+ return 'offer-header-card__status offer-header-card__status--positive';
4573
4671
  if (this.warn.has(status))
4574
- return 'bg-yellow-50 text-yellow-800 border-yellow-200';
4672
+ return 'offer-header-card__status offer-header-card__status--warning';
4575
4673
  if (this.bad.has(status))
4576
- return 'bg-red-50 text-red-700 border-red-200';
4674
+ return 'offer-header-card__status offer-header-card__status--negative';
4577
4675
  // DRAFT or unknown
4578
- return 'bg-gray-50 text-gray-700 border-gray-200';
4676
+ return 'offer-header-card__status offer-header-card__status--neutral';
4579
4677
  }
4580
4678
  statusHint(status) {
4581
4679
  if (!status)
@@ -4635,270 +4733,215 @@ class OfferHeaderCard {
4635
4733
  return '';
4636
4734
  }
4637
4735
  }
4736
+ typeLabel(value) {
4737
+ if (!value) {
4738
+ return 'Offer';
4739
+ }
4740
+ return `${String(value).toLowerCase().replace(/^\w/, (char) => char.toUpperCase())} offer`;
4741
+ }
4638
4742
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
4639
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHeaderCard, isStandalone: true, selector: "rolatech-offer-header-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between\">\n <div class=\"space-y-1\">\n <h1 class=\"text-xl font-semibold text-(--rt-text-primary)\">Offer #{{ o.id }}</h1>\n <div class=\"text-sm text-(--rt-text-secondary)\">\n <span class=\"font-medium\">{{ o.type }}</span>\n <span class=\"mx-2\">\u2022</span>\n <span class=\"font-medium\" [ngClass]=\"statusBadgeClass(o.status)\">{{ o.status }}</span>\n </div>\n\n <div class=\"flex flex-wrap gap-2 text-xs pt-2\">\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\"> userId: {{ ctx.asText(o.userId) }} </span>\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\"> agentId: {{ ctx.asText(o.agentId) }} </span>\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\">\n propertyId: {{ ctx.asText(o.propertyId) }}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\">\n invoice: {{ ctx.invoiceOptionLabel[o.invoiceOption] }}\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <div>\n <ng-content></ng-content>\n </div>\n </div>\n</section>\n}\n", styles: [""], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
4743
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHeaderCard, isStandalone: true, selector: "rolatech-offer-header-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-header-card\">\n <div class=\"offer-header-card__body\">\n <div class=\"offer-header-card__copy\">\n <span class=\"offer-header-card__eyebrow\">{{ typeLabel(o.type) }}</span>\n\n <div class=\"offer-header-card__heading\">\n <div class=\"offer-header-card__heading-copy\">\n <h2 class=\"offer-header-card__title\">Offer #{{ o.id }}</h2>\n <p class=\"offer-header-card__description\">{{ statusHint(o.status) }}</p>\n </div>\n\n <span [ngClass]=\"statusBadgeClass(o.status)\">{{ offerStatusLabel(o.status) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__facts\">\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Offer type</span>\n <strong class=\"offer-header-card__fact-value\">{{ typeLabel(o.type) }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Invoice style</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">{{ o.type === 'RENTAL' ? 'Applicants' : 'Payment method' }}</span>\n <strong class=\"offer-header-card__fact-value\">\n @if (o.type === 'RENTAL') {\n {{ o.tenants?.length ?? 0 }}\n } @else {\n {{ ctx.asText(o.sale?.paymentMethod) }}\n }\n </strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Property reference</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.asText(o.propertyId) }}</strong>\n </article>\n </div>\n\n <div class=\"offer-header-card__meta\">\n <span class=\"offer-header-card__meta-chip\">Applicant ID {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Agent ID {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Property ID {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__actions\">\n <ng-content></ng-content>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-header-card{display:grid;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 44%),linear-gradient(140deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%)),var(--rt-base-background, #ffffff);box-shadow:0 22px 52px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.offer-header-card__body{display:flex;flex-direction:column;gap:1rem}.offer-header-card__copy,.offer-header-card__heading-copy{display:flex;flex-direction:column;gap:.55rem}.offer-header-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.38rem .72rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-header-card__heading{display:flex;flex-direction:column;gap:.85rem}.offer-header-card__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.3rem,2vw,1.75rem);line-height:1.1;font-weight:800}.offer-header-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-header-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.4rem;padding:.45rem .85rem;border:1px solid transparent;border-radius:999px;font-size:.85rem;font-weight:700}.offer-header-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-header-card__status--warning{border-color:#f59e0b47;background:#f59e0b1f;color:#a16207}.offer-header-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-header-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);color:var(--rt-text-secondary)}.offer-header-card__facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-header-card__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-header-card__fact-label{color:var(--rt-text-secondary);font-size:.78rem}.offer-header-card__fact-value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.35}.offer-header-card__meta{display:flex;flex-wrap:wrap;gap:.55rem}.offer-header-card__meta-chip{display:inline-flex;align-items:center;min-height:2rem;padding:.4rem .72rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-header-card__actions{display:flex;align-items:flex-start;justify-content:flex-end}@media(min-width:960px){.offer-header-card{grid-template-columns:minmax(0,1.7fr) auto;align-items:start}.offer-header-card__heading{flex-direction:row;align-items:flex-start;justify-content:space-between}.offer-header-card__facts{grid-template-columns:repeat(4,minmax(0,1fr))}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
4640
4744
  }
4641
4745
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, decorators: [{
4642
4746
  type: Component,
4643
- args: [{ selector: 'rolatech-offer-header-card', imports: [NgClass], template: "@if (ctx.offer(); as o) {\n<section class=\"flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between\">\n <div class=\"space-y-1\">\n <h1 class=\"text-xl font-semibold text-(--rt-text-primary)\">Offer #{{ o.id }}</h1>\n <div class=\"text-sm text-(--rt-text-secondary)\">\n <span class=\"font-medium\">{{ o.type }}</span>\n <span class=\"mx-2\">\u2022</span>\n <span class=\"font-medium\" [ngClass]=\"statusBadgeClass(o.status)\">{{ o.status }}</span>\n </div>\n\n <div class=\"flex flex-wrap gap-2 text-xs pt-2\">\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\"> userId: {{ ctx.asText(o.userId) }} </span>\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\"> agentId: {{ ctx.asText(o.agentId) }} </span>\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\">\n propertyId: {{ ctx.asText(o.propertyId) }}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border border-(--rt-border-color) bg-(--rt-raised-background)\">\n invoice: {{ ctx.invoiceOptionLabel[o.invoiceOption] }}\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <div>\n <ng-content></ng-content>\n </div>\n </div>\n</section>\n}\n" }]
4747
+ args: [{ selector: 'rolatech-offer-header-card', imports: [NgClass], template: "@if (ctx.offer(); as o) {\n<section class=\"offer-header-card\">\n <div class=\"offer-header-card__body\">\n <div class=\"offer-header-card__copy\">\n <span class=\"offer-header-card__eyebrow\">{{ typeLabel(o.type) }}</span>\n\n <div class=\"offer-header-card__heading\">\n <div class=\"offer-header-card__heading-copy\">\n <h2 class=\"offer-header-card__title\">Offer #{{ o.id }}</h2>\n <p class=\"offer-header-card__description\">{{ statusHint(o.status) }}</p>\n </div>\n\n <span [ngClass]=\"statusBadgeClass(o.status)\">{{ offerStatusLabel(o.status) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__facts\">\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Offer type</span>\n <strong class=\"offer-header-card__fact-value\">{{ typeLabel(o.type) }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Invoice style</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">{{ o.type === 'RENTAL' ? 'Applicants' : 'Payment method' }}</span>\n <strong class=\"offer-header-card__fact-value\">\n @if (o.type === 'RENTAL') {\n {{ o.tenants?.length ?? 0 }}\n } @else {\n {{ ctx.asText(o.sale?.paymentMethod) }}\n }\n </strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Property reference</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.asText(o.propertyId) }}</strong>\n </article>\n </div>\n\n <div class=\"offer-header-card__meta\">\n <span class=\"offer-header-card__meta-chip\">Applicant ID {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Agent ID {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Property ID {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__actions\">\n <ng-content></ng-content>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-header-card{display:grid;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 44%),linear-gradient(140deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%)),var(--rt-base-background, #ffffff);box-shadow:0 22px 52px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.offer-header-card__body{display:flex;flex-direction:column;gap:1rem}.offer-header-card__copy,.offer-header-card__heading-copy{display:flex;flex-direction:column;gap:.55rem}.offer-header-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.38rem .72rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-header-card__heading{display:flex;flex-direction:column;gap:.85rem}.offer-header-card__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.3rem,2vw,1.75rem);line-height:1.1;font-weight:800}.offer-header-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-header-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.4rem;padding:.45rem .85rem;border:1px solid transparent;border-radius:999px;font-size:.85rem;font-weight:700}.offer-header-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-header-card__status--warning{border-color:#f59e0b47;background:#f59e0b1f;color:#a16207}.offer-header-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-header-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);color:var(--rt-text-secondary)}.offer-header-card__facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-header-card__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-header-card__fact-label{color:var(--rt-text-secondary);font-size:.78rem}.offer-header-card__fact-value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.35}.offer-header-card__meta{display:flex;flex-wrap:wrap;gap:.55rem}.offer-header-card__meta-chip{display:inline-flex;align-items:center;min-height:2rem;padding:.4rem .72rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-header-card__actions{display:flex;align-items:flex-start;justify-content:flex-end}@media(min-width:960px){.offer-header-card{grid-template-columns:minmax(0,1.7fr) auto;align-items:start}.offer-header-card__heading{flex-direction:row;align-items:flex-start;justify-content:space-between}.offer-header-card__facts{grid-template-columns:repeat(4,minmax(0,1fr))}}\n"] }]
4644
4748
  }] });
4645
4749
 
4646
4750
  class OfferSideSummaryCard {
4647
4751
  ctx = inject(OfferDetailContext);
4752
+ statusLabel = offerStatusLabel;
4648
4753
  salePaymentMethodLabel = {
4649
4754
  CASH: 'Cash',
4650
4755
  MORTGAGE: 'Mortgage',
4651
4756
  };
4757
+ typeLabel(value) {
4758
+ if (!value) {
4759
+ return 'Offer';
4760
+ }
4761
+ return `${String(value).toLowerCase().replace(/^\w/, (char) => char.toUpperCase())} offer`;
4762
+ }
4763
+ statusToneClass(status) {
4764
+ switch (status) {
4765
+ case PropertyOfferStatus$1.ACCEPTED:
4766
+ case PropertyOfferStatus$1.REFERENCES_PASSED:
4767
+ case PropertyOfferStatus$1.CONTRACT_SIGNED:
4768
+ case PropertyOfferStatus$1.MOVE_IN_PAYMENT_PAID:
4769
+ case PropertyOfferStatus$1.COMPLETED:
4770
+ return 'offer-side-summary-card__status offer-side-summary-card__status--positive';
4771
+ case PropertyOfferStatus$1.REJECTED:
4772
+ case PropertyOfferStatus$1.REFERENCES_FAILED:
4773
+ case PropertyOfferStatus$1.CONTRACT_FAILED:
4774
+ case PropertyOfferStatus$1.CANCELLED:
4775
+ case PropertyOfferStatus$1.FAILED:
4776
+ case PropertyOfferStatus$1.EXPIRED:
4777
+ return 'offer-side-summary-card__status offer-side-summary-card__status--negative';
4778
+ default:
4779
+ return 'offer-side-summary-card__status offer-side-summary-card__status--neutral';
4780
+ }
4781
+ }
4782
+ leadAmount(offer) {
4783
+ if (offer.type === 'RENTAL') {
4784
+ return offer.rentalTerms?.amount ?? offer.item.amount;
4785
+ }
4786
+ return offer.sale?.amount ?? offer.item.amount;
4787
+ }
4788
+ nextStepCopy(offer) {
4789
+ switch (offer.status) {
4790
+ case PropertyOfferStatus$1.SUBMITTED:
4791
+ case PropertyOfferStatus$1.VIEWED_BY_AGENT:
4792
+ case PropertyOfferStatus$1.VIEWED_BY_TENANT:
4793
+ return 'The latest offer is waiting for a response.';
4794
+ case PropertyOfferStatus$1.COUNTERED_BY_AGENT:
4795
+ case PropertyOfferStatus$1.COUNTERED_BY_TENANT:
4796
+ return 'Compare the latest version against the previous one before responding.';
4797
+ case PropertyOfferStatus$1.ACCEPTED:
4798
+ case PropertyOfferStatus$1.UNDER_OFFER:
4799
+ return 'The negotiation has moved forward into the next workflow step.';
4800
+ case PropertyOfferStatus$1.REFERENCING:
4801
+ return 'Referencing checks are in progress.';
4802
+ case PropertyOfferStatus$1.COMPLETED:
4803
+ return 'The offer flow is complete and kept here as a record.';
4804
+ default:
4805
+ return 'Use this summary to keep the main terms and references visible while reviewing the detail page.';
4806
+ }
4807
+ }
4652
4808
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
4653
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Summary</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Key fields</div>\n </div>\n\n <div class=\"grid grid-cols-1 gap-3 text-sm\">\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Type</span><span class=\"font-medium\">{{ o.type }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Status</span><span class=\"font-medium\">{{ o.status }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Invoice</span\n ><span class=\"font-medium\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Tenants</span><span class=\"font-medium\">{{ o.tenants?.length ?? 0 }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Item amount</span><span class=\"font-medium\">{{ o.item.amount | price }}</span>\n </div>\n @if (ctx.isRental()) {\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-(--rt-text-secondary)\">Tenants</span>\n <span class=\"font-medium\">{{ ctx.asText(o.tenants?.length ?? 0) }}</span>\n </div>\n }\n </div>\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }] });
4809
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-side-summary-card\">\n <div class=\"offer-side-summary-card__hero\">\n <span class=\"offer-side-summary-card__eyebrow\">Quick summary</span>\n <div class=\"offer-side-summary-card__amount\">{{ leadAmount(o) | price }}</div>\n <p class=\"offer-side-summary-card__description\">{{ nextStepCopy(o) }}</p>\n <span [class]=\"statusToneClass(o.status)\">{{ statusLabel(o.status) }}</span>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Offer details</h3>\n <div class=\"offer-side-summary-card__rows\">\n <div class=\"offer-side-summary-card__row\">\n <span>Type</span>\n <strong>{{ typeLabel(o.type) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Invoice</span>\n <strong>{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Listing amount</span>\n <strong>{{ o.item.amount | price }}</strong>\n </div>\n @if (ctx.isRental()) {\n <div class=\"offer-side-summary-card__row\">\n <span>Applicants</span>\n <strong>{{ o.tenants?.length ?? 0 }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Move-in</span>\n <strong>{{ o.rentalTerms?.moveInDate ? (o.rentalTerms?.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </div>\n } @else {\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer method</span>\n <strong>{{ salePaymentMethodLabel[o.sale?.paymentMethod!] || ctx.asText(o.sale?.paymentMethod) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer</span>\n <strong>{{ o.sale?.buyerName ?? '\u2014' }}</strong>\n </div>\n }\n </div>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Route references</h3>\n <div class=\"offer-side-summary-card__chips\">\n <span class=\"offer-side-summary-card__chip\">User {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Agent {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Property {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-side-summary-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.05rem;background:linear-gradient(150deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%))}.offer-side-summary-card__hero,.offer-side-summary-card__section{display:flex;flex-direction:column;gap:.6rem}.offer-side-summary-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-side-summary-card__amount{color:var(--rt-text-primary);font-size:clamp(1.55rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-side-summary-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-side-summary-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2rem;padding:.35rem .7rem;border:1px solid transparent;border-radius:999px;font-size:.8rem;font-weight:700}.offer-side-summary-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-side-summary-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-side-summary-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-base-background, #ffffff);color:var(--rt-text-primary)}.offer-side-summary-card__section{padding-top:.9rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-side-summary-card__section-title{margin:0;color:var(--rt-text-primary);font-size:.96rem;font-weight:720}.offer-side-summary-card__rows{display:flex;flex-direction:column;gap:.7rem}.offer-side-summary-card__row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;color:var(--rt-text-secondary);font-size:.88rem}.offer-side-summary-card__row strong{color:var(--rt-text-primary);text-align:right}.offer-side-summary-card__chips{display:flex;flex-wrap:wrap;gap:.5rem}.offer-side-summary-card__chip{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] });
4654
4810
  }
4655
4811
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, decorators: [{
4656
4812
  type: Component,
4657
- args: [{ selector: 'rolatech-offer-side-summary-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-(--rt-border-color) p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Summary</div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Key fields</div>\n </div>\n\n <div class=\"grid grid-cols-1 gap-3 text-sm\">\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Type</span><span class=\"font-medium\">{{ o.type }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Status</span><span class=\"font-medium\">{{ o.status }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Invoice</span\n ><span class=\"font-medium\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Tenants</span><span class=\"font-medium\">{{ o.tenants?.length ?? 0 }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Item amount</span><span class=\"font-medium\">{{ o.item.amount | price }}</span>\n </div>\n @if (ctx.isRental()) {\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-(--rt-text-secondary)\">Tenants</span>\n <span class=\"font-medium\">{{ ctx.asText(o.tenants?.length ?? 0) }}</span>\n </div>\n }\n </div>\n</section>\n}\n" }]
4813
+ args: [{ selector: 'rolatech-offer-side-summary-card', imports: [CommonModule, PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-side-summary-card\">\n <div class=\"offer-side-summary-card__hero\">\n <span class=\"offer-side-summary-card__eyebrow\">Quick summary</span>\n <div class=\"offer-side-summary-card__amount\">{{ leadAmount(o) | price }}</div>\n <p class=\"offer-side-summary-card__description\">{{ nextStepCopy(o) }}</p>\n <span [class]=\"statusToneClass(o.status)\">{{ statusLabel(o.status) }}</span>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Offer details</h3>\n <div class=\"offer-side-summary-card__rows\">\n <div class=\"offer-side-summary-card__row\">\n <span>Type</span>\n <strong>{{ typeLabel(o.type) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Invoice</span>\n <strong>{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Listing amount</span>\n <strong>{{ o.item.amount | price }}</strong>\n </div>\n @if (ctx.isRental()) {\n <div class=\"offer-side-summary-card__row\">\n <span>Applicants</span>\n <strong>{{ o.tenants?.length ?? 0 }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Move-in</span>\n <strong>{{ o.rentalTerms?.moveInDate ? (o.rentalTerms?.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </div>\n } @else {\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer method</span>\n <strong>{{ salePaymentMethodLabel[o.sale?.paymentMethod!] || ctx.asText(o.sale?.paymentMethod) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer</span>\n <strong>{{ o.sale?.buyerName ?? '\u2014' }}</strong>\n </div>\n }\n </div>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Route references</h3>\n <div class=\"offer-side-summary-card__chips\">\n <span class=\"offer-side-summary-card__chip\">User {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Agent {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Property {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-side-summary-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.05rem;background:linear-gradient(150deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%))}.offer-side-summary-card__hero,.offer-side-summary-card__section{display:flex;flex-direction:column;gap:.6rem}.offer-side-summary-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-side-summary-card__amount{color:var(--rt-text-primary);font-size:clamp(1.55rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-side-summary-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-side-summary-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2rem;padding:.35rem .7rem;border:1px solid transparent;border-radius:999px;font-size:.8rem;font-weight:700}.offer-side-summary-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-side-summary-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-side-summary-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-base-background, #ffffff);color:var(--rt-text-primary)}.offer-side-summary-card__section{padding-top:.9rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-side-summary-card__section-title{margin:0;color:var(--rt-text-primary);font-size:.96rem;font-weight:720}.offer-side-summary-card__rows{display:flex;flex-direction:column;gap:.7rem}.offer-side-summary-card__row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;color:var(--rt-text-secondary);font-size:.88rem}.offer-side-summary-card__row strong{color:var(--rt-text-primary);text-align:right}.offer-side-summary-card__chips{display:flex;flex-wrap:wrap;gap:.5rem}.offer-side-summary-card__chip{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}\n"] }]
4658
4814
  }] });
4659
4815
 
4660
- const MY_FORMATS$4 = {
4661
- parse: {
4662
- dateInput: 'YYYY-MM-DD',
4663
- },
4664
- display: {
4665
- dateInput: 'YYYY-MM-DD',
4666
- monthYearLabel: 'MMM YYYY',
4667
- dateA11yLabel: 'YYYY-MM-DD',
4668
- monthYearA11yLabel: 'MMMM YYYY',
4669
- },
4670
- };
4816
+ class PropertyWorkspaceShell {
4817
+ eyebrow = input('Workspace', ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
4818
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
4819
+ description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
4820
+ stats = input([], ...(ngDevMode ? [{ debugName: "stats" }] : []));
4821
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
4822
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyWorkspaceShell, isStandalone: true, selector: "rolatech-property-workspace-shell", inputs: { eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, stats: { classPropertyName: "stats", publicName: "stats", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
4823
+ }
4824
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, decorators: [{
4825
+ type: Component,
4826
+ args: [{ selector: 'rolatech-property-workspace-shell', imports: [CommonModule], template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"] }]
4827
+ }], propDecorators: { eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], stats: [{ type: i0.Input, args: [{ isSignal: true, alias: "stats", required: false }] }] } });
4828
+
4671
4829
  class PropertyOfferDetailComponent extends BaseComponent {
4672
- propertyService = inject(PropertyService);
4673
4830
  propertyOfferService = inject(PropertyOfferService);
4674
- propertyOfferCounterService = inject(PropertyOfferCounterService);
4675
4831
  f = inject(PropertyOfferNegotiationFacade);
4676
- authUserService = inject(AuthUserService);
4677
- paymentService = inject(PaymentService);
4678
4832
  ctx = inject(OfferDetailContext);
4679
- clipboard = inject(Clipboard);
4680
- isRental = computed(() => this.offer.type.toString() === 'RENTAL', ...(ngDevMode ? [{ debugName: "isRental" }] : []));
4681
- isSale = computed(() => this.offer.type.toString() === 'SALE', ...(ngDevMode ? [{ debugName: "isSale" }] : []));
4682
- expandedTenantIndexes = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedTenantIndexes" }] : []));
4683
- view;
4684
- offer;
4685
- info = false;
4686
- loadingTimeline = false;
4687
- timelineData;
4688
- status = PropertyOfferStatus;
4689
- timelineStatus = PropertyOfferTimelineStatus;
4690
- env = inject(APP_CONFIG);
4691
- location = inject(Location);
4692
- statusLabel = computed(() => offerStatusLabel(this.f.offer()?.status), ...(ngDevMode ? [{ debugName: "statusLabel" }] : []));
4693
- property;
4694
- agent;
4695
- checkouting = false;
4696
- holdingDeposit;
4697
- securityDeposit;
4698
- type = computed(() => {
4699
- return this.offer.type.toString();
4700
- }, ...(ngDevMode ? [{ debugName: "type" }] : []));
4701
- async ngOnInit() {
4702
- this.route.params.subscribe((params) => {
4703
- const id = params['id'];
4704
- this.ctx.init(id);
4705
- if (id)
4706
- this.f.load(id, 'tenant');
4707
- this.get(id);
4708
- // this.getViewByTenant(id);
4709
- });
4710
- this.route.queryParams.subscribe(async (params) => {
4711
- const sessionId = params['session_id'];
4712
- if (sessionId) {
4713
- this.propertyOfferService.checkOfferPaymentStatus(this.id, sessionId).subscribe({
4714
- next: (res) => {
4715
- if (res.status === 'paid') {
4716
- this.offer.status = PropertyOfferStatus.HOLDING_DEPOSIT_PAID;
4717
- // Show success UI
4718
- }
4719
- },
4720
- });
4721
- }
4722
- });
4723
- }
4724
- ngOnDestroy() {
4725
- this.checkouting = false;
4726
- }
4727
- get(id) {
4728
- this.propertyOfferService.getOffer(id).subscribe({
4729
- next: (res) => {
4730
- this.offer = res.data;
4731
- this.getProperty(this.offer.propertyId);
4732
- this.calculateDeposit();
4733
- // this.findAgentInfo();
4734
- },
4735
- });
4736
- }
4737
- getViewByTenant(id) {
4738
- this.propertyOfferCounterService.getNegotiationViewByTenant(id).subscribe({
4739
- next: (res) => {
4740
- this.view = res.data;
4741
- },
4742
- });
4743
- }
4744
- timeline() {
4745
- this.info = true;
4746
- this.loadingTimeline = true;
4747
- this.propertyOfferService.offerTimeline(this.id).subscribe({
4748
- next: (res) => {
4749
- this.timelineData = res.data;
4750
- this.loadingTimeline = false;
4751
- },
4752
- error: (error) => {
4753
- this.loadingTimeline = false;
4754
- },
4755
- });
4756
- }
4757
- getProperty(propertyId) {
4758
- this.propertyService.get(propertyId).subscribe({
4759
- next: (res) => {
4760
- this.property = res.data;
4761
- this.getAgentPublicInfo(this.property.agentId);
4762
- },
4763
- });
4764
- }
4765
- getAgentPublicInfo(agentId) {
4766
- this.authUserService.getPublicUserInfo(agentId).subscribe({
4767
- next: (res) => {
4768
- this.agent = res;
4769
- },
4770
- });
4771
- }
4772
- cancel() {
4773
- this.propertyOfferService.cancelOffer(this.id).subscribe({
4774
- next: (res) => {
4775
- this.offer.status = 'CANCELLED';
4776
- this.snackBarService.open('Cancelled');
4777
- },
4778
- error: (error) => {
4779
- this.snackBarService.open(error.message);
4780
- },
4781
- });
4782
- }
4783
- calculateDeposit() {
4784
- const price = this.offer.item.amount;
4785
- this.holdingDeposit = ((price * 12) / 52).toFixed(2);
4786
- this.securityDeposit = (((price * 12) / 52) * 5).toFixed(2);
4787
- }
4788
- holdingDepositCheckout() {
4789
- this.checkouting = true;
4790
- const data = {
4791
- businessType: 'OFFER',
4792
- businessId: this.id,
4793
- provider: 'STRIPE',
4794
- method: 'STRIPE_CHECKOUT',
4795
- purpose: 'HOLDING_DEPOSIT',
4796
- };
4797
- this.paymentService
4798
- .createPaymentIntent(data)
4799
- .pipe(switchMap((res) => {
4800
- const intent = {
4801
- intentId: res.data.id,
4802
- provider: 'STRIPE',
4803
- method: 'STRIPE_CHECKOUT',
4804
- successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
4805
- cancelUrl: window.location.href,
4806
- };
4807
- return this.paymentService.createPayment(intent);
4808
- }))
4809
- .subscribe({
4810
- next: (res) => {
4811
- this.checkouting = false;
4812
- const { checkoutUrl } = res.data;
4813
- window.open(checkoutUrl, '_blank');
4814
- },
4815
- error: (error) => {
4816
- this.checkouting = false;
4817
- },
4818
- });
4819
- }
4820
- securityDepositCheckout() {
4821
- this.checkouting = true;
4822
- const data = {
4823
- successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
4824
- cancelUrl: window.location.href,
4825
- };
4826
- this.propertyOfferService.createSecurityDepositCheckout(this.id, data).subscribe({
4827
- next: (res) => {
4828
- window.location.href = res.data.checkoutUrl;
4829
- },
4830
- error: (error) => {
4831
- this.checkouting = false;
4833
+ mode = input('tenant', ...(ngDevMode ? [{ debugName: "mode" }] : []));
4834
+ eyebrow = input(null, ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
4835
+ title = input(null, ...(ngDevMode ? [{ debugName: "title" }] : []));
4836
+ description = input(null, ...(ngDevMode ? [{ debugName: "description" }] : []));
4837
+ backLabel = input(null, ...(ngDevMode ? [{ debugName: "backLabel" }] : []));
4838
+ currentOfferId = this.id ?? null;
4839
+ statusLabel = computed(() => offerStatusLabel(this.ctx.offer()?.status), ...(ngDevMode ? [{ debugName: "statusLabel" }] : []));
4840
+ resolvedEyebrow = computed(() => this.eyebrow() ?? (this.mode() === 'agent' ? 'Market workflow' : 'My applications'), ...(ngDevMode ? [{ debugName: "resolvedEyebrow" }] : []));
4841
+ resolvedTitle = computed(() => this.title() ?? (this.mode() === 'agent' ? 'Offer detail' : 'Offer detail'), ...(ngDevMode ? [{ debugName: "resolvedTitle" }] : []));
4842
+ resolvedDescription = computed(() => this.description() ??
4843
+ (this.mode() === 'agent'
4844
+ ? 'Track buyer-side negotiations, compare counter-offers, and keep the deal moving from one shared workspace.'
4845
+ : 'Review every negotiation update, compare offer versions, and keep the next step close at hand.'), ...(ngDevMode ? [{ debugName: "resolvedDescription" }] : []));
4846
+ resolvedBackLabel = computed(() => this.backLabel() ?? (this.mode() === 'agent' ? 'Back to buyer offers' : 'Back to offers'), ...(ngDevMode ? [{ debugName: "resolvedBackLabel" }] : []));
4847
+ stats = computed(() => {
4848
+ const offer = this.ctx.offer();
4849
+ if (!offer) {
4850
+ return [
4851
+ { label: 'Status', value: 'Loading', hint: 'Checking the current negotiation state' },
4852
+ { label: 'Type', value: 'Loading', hint: 'Resolving the submitted offer type' },
4853
+ { label: 'Applicants', value: 'Loading', hint: 'Looking up the latest submitted party count' },
4854
+ { label: 'Offer amount', value: 'Loading', hint: 'Fetching the latest property snapshot' },
4855
+ ];
4856
+ }
4857
+ const isRental = offer.type === 'RENTAL';
4858
+ return [
4859
+ {
4860
+ label: 'Status',
4861
+ value: this.statusLabel(),
4862
+ hint: this.mode() === 'agent' ? 'Latest buyer-side negotiation state' : 'Latest response from the listing side',
4832
4863
  },
4833
- });
4834
- }
4835
- copyText(redactContact) {
4836
- this.propertyOfferService.getCopyText(this.id).subscribe({
4837
- next: (res) => {
4838
- this.clipboard.copy(res.data.text);
4839
- this.snackBarService.open('Copied');
4864
+ {
4865
+ label: 'Type',
4866
+ value: this.humanize(offer.type),
4867
+ hint: isRental ? 'Rental negotiation workflow' : 'Sale negotiation workflow',
4868
+ },
4869
+ isRental
4870
+ ? {
4871
+ label: 'Applicants',
4872
+ value: offer.tenants?.length ?? 0,
4873
+ hint: 'People included in this submission',
4874
+ }
4875
+ : {
4876
+ label: 'Funding',
4877
+ value: this.humanize(offer.sale?.paymentMethod),
4878
+ hint: 'Declared buyer payment method',
4879
+ },
4880
+ {
4881
+ label: 'Offer amount',
4882
+ value: this.formatMoney(offer.item?.amount),
4883
+ hint: offer.item?.title || 'Property snapshot attached to this offer',
4840
4884
  },
4841
- });
4842
- }
4843
- reject() {
4844
- const ref = this.dialog.open(RejectDialogComponent, {
4845
- width: '420px',
4846
- });
4847
- ref.afterClosed().subscribe((notes) => {
4848
- if (notes) {
4849
- this.f.reject(notes);
4850
- }
4851
- });
4852
- }
4853
- openReferenceFailedDialog() {
4854
- const ref = this.dialog.open(RejectDialogComponent, {
4855
- width: '420px',
4856
- });
4857
- ref.afterClosed().subscribe((notes) => {
4858
- if (notes) {
4859
- this.f.markReferencesFailed(notes);
4885
+ ];
4886
+ }, ...(ngDevMode ? [{ debugName: "stats" }] : []));
4887
+ ngOnInit() {
4888
+ this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
4889
+ const id = params.get('id');
4890
+ this.currentOfferId = id;
4891
+ if (!id) {
4892
+ return;
4860
4893
  }
4894
+ this.ctx.init(id);
4895
+ void this.f.load(id, this.mode());
4861
4896
  });
4862
- }
4863
- //COUNTER
4864
- counter() {
4865
- const options = {
4866
- width: '500px',
4867
- title: 'Counter offer',
4868
- height: '90%',
4869
- cancelText: 'Cancel',
4870
- confirmText: 'Confirm',
4871
- component: OfferCounterDialog,
4872
- data: {
4873
- term: this.offer.rentalTerms,
4874
- },
4875
- };
4876
- this.dialogService.open(options);
4877
- this.dialogService.confirmed().subscribe((res) => {
4878
- if (res) {
4879
- console.log(res);
4880
- this.propertyOfferCounterService.counterByTenant(this.id, res).subscribe({
4881
- next: (res) => {
4882
- this.snackBarService.open('Counter sent');
4883
- },
4884
- error: (error) => {
4885
- this.snackBarService.open(error.message);
4886
- },
4887
- });
4897
+ this.route.queryParamMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
4898
+ const sessionId = params.get('session_id');
4899
+ const offerId = this.currentOfferId;
4900
+ if (!offerId || !sessionId) {
4901
+ return;
4888
4902
  }
4903
+ this.propertyOfferService.checkOfferPaymentStatus(offerId, sessionId).subscribe({
4904
+ next: (res) => {
4905
+ if (res.status === 'paid') {
4906
+ void this.ctx.reloadOffer(offerId);
4907
+ void this.ctx.reloadHistory(offerId);
4908
+ void this.f.load(offerId, this.mode());
4909
+ }
4910
+ },
4911
+ });
4889
4912
  });
4890
4913
  }
4891
- withdraw() { }
4914
+ formatMoney(amount) {
4915
+ if (amount === null || amount === undefined) {
4916
+ return '—';
4917
+ }
4918
+ return new Intl.NumberFormat('en-GB', {
4919
+ style: 'currency',
4920
+ currency: 'GBP',
4921
+ maximumFractionDigits: 0,
4922
+ }).format(amount);
4923
+ }
4924
+ humanize(value) {
4925
+ if (!value) {
4926
+ return '—';
4927
+ }
4928
+ return value
4929
+ .trim()
4930
+ .replace(/[_-]+/g, ' ')
4931
+ .toLowerCase()
4932
+ .replace(/\b\w/g, (char) => char.toUpperCase());
4933
+ }
4892
4934
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4893
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade], usesInheritance: true, ngImport: i0, template: "@if (ctx.offer(); as o) {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-6 space-y-4\">\n <rolatech-offer-header-card> </rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4\">\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4\">\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <!-- offer history -->\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"lg:col-span-4 space-y-4\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n </div>\n} @else {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-10\">\n <div class=\"rounded-2xl border border-(--rt-border-color) bg-(--rolatech-raised-background) p-6\">Loading\u2026</div>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: OfferHeaderCard, selector: "rolatech-offer-header-card" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferSideSummaryCard, selector: "rolatech-offer-side-summary-card" }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }] });
4935
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null } }, providers: [OfferDetailContext, PropertyOfferNegotiationFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-offer-detail\">\n @if (mode() === 'agent') {\n <rolatech-property-workspace-shell\n [eyebrow]=\"resolvedEyebrow()\"\n [title]=\"resolvedTitle()\"\n [description]=\"resolvedDescription()\"\n [stats]=\"stats()\"\n >\n <div workspace-actions class=\"property-offer-detail__workspace-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-property-workspace-shell>\n } @else {\n <rolatech-page-collection-shell [eyebrow]=\"resolvedEyebrow()\" [title]=\"resolvedTitle()\" [subtitle]=\"resolvedDescription()\">\n <div page-shell-header-meta class=\"property-offer-detail__header-meta\">\n <div class=\"property-offer-detail__page-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <div class=\"property-offer-detail__summary\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-offer-detail__summary-card\">\n <span class=\"property-offer-detail__summary-value\">{{ stat.value }}</span>\n <span class=\"property-offer-detail__summary-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-offer-detail__summary-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-page-collection-shell>\n }\n\n <ng-template #detailContent>\n @if (ctx.error()) {\n <section class=\"property-offer-detail__error\">{{ ctx.error() }}</section>\n }\n\n @if (ctx.offer()) {\n <div class=\"property-offer-detail__grid\">\n <div class=\"property-offer-detail__main\">\n <rolatech-offer-header-card></rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"property-offer-detail__side\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n } @else {\n <section class=\"property-offer-detail__loading\">Loading offer detail...</section>\n }\n </ng-template>\n</div>\n", styles: [":host{display:block}.property-offer-detail__workspace-actions{display:flex;justify-content:flex-end}.property-offer-detail__header-meta{display:flex;flex-direction:column;gap:.85rem}.property-offer-detail__page-actions{display:flex;justify-content:flex-end}.property-offer-detail__summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:.85rem}.property-offer-detail__summary-card{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-offer-detail__summary-label,.property-offer-detail__summary-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-offer-detail__grid{display:grid;gap:1rem}.property-offer-detail__main,.property-offer-detail__side{display:flex;flex-direction:column;gap:1rem}.property-offer-detail__error,.property-offer-detail__loading{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:var(--rt-base-background, #ffffff);padding:1rem 1.1rem;color:var(--rt-text-secondary)}.property-offer-detail__error{color:#b91c1c;background:color-mix(in srgb,#fff1f2 84%,var(--rt-base-background, #ffffff))}@media(min-width:1024px){.property-offer-detail__summary{grid-template-columns:repeat(4,minmax(0,1fr))}.property-offer-detail__grid{grid-template-columns:minmax(0,1.75fr) minmax(20rem,.9fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: PageCollectionShellComponent, selector: "rolatech-page-collection-shell", inputs: ["eyebrow", "title", "subtitle"] }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }, { kind: "component", type: OfferHeaderCard, selector: "rolatech-offer-header-card" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferSideSummaryCard, selector: "rolatech-offer-side-summary-card" }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }] });
4894
4936
  }
4895
4937
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, decorators: [{
4896
4938
  type: Component,
4897
4939
  args: [{ selector: 'rolatech-property-offer-detail', imports: [
4898
4940
  CommonModule,
4941
+ RouterLink,
4899
4942
  MatButtonModule,
4900
- MatIconModule,
4901
- MatProgressSpinnerModule,
4943
+ PageCollectionShellComponent,
4944
+ PropertyWorkspaceShell,
4902
4945
  OfferHeaderCard,
4903
4946
  OfferItemCard,
4904
4947
  OfferRentalTermsCard,
@@ -4906,47 +4949,195 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4906
4949
  OfferTenantsAccordion,
4907
4950
  OfferSideSummaryCard,
4908
4951
  OfferHistoryAccordion,
4909
- MatFormFieldModule,
4910
- MatInputModule,
4911
- MatSelectModule,
4912
- MatDatepickerModule,
4913
- MatNativeDateModule,
4914
- MatSlideToggleModule,
4915
- MatChipsModule,
4916
- FormsModule,
4917
4952
  OfferCounterPreviousCard,
4918
4953
  OfferCounterLatestCard,
4919
4954
  OfferNegotiationSection,
4920
- ], providers: [OfferDetailContext, PropertyOfferNegotiationFacade], template: "@if (ctx.offer(); as o) {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-6 space-y-4\">\n <rolatech-offer-header-card> </rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4\">\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4\">\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <!-- offer history -->\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"lg:col-span-4 space-y-4\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n </div>\n} @else {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-10\">\n <div class=\"rounded-2xl border border-(--rt-border-color) bg-(--rolatech-raised-background) p-6\">Loading\u2026</div>\n </div>\n}\n" }]
4921
- }] });
4955
+ ], providers: [OfferDetailContext, PropertyOfferNegotiationFacade], template: "<div class=\"property-offer-detail\">\n @if (mode() === 'agent') {\n <rolatech-property-workspace-shell\n [eyebrow]=\"resolvedEyebrow()\"\n [title]=\"resolvedTitle()\"\n [description]=\"resolvedDescription()\"\n [stats]=\"stats()\"\n >\n <div workspace-actions class=\"property-offer-detail__workspace-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-property-workspace-shell>\n } @else {\n <rolatech-page-collection-shell [eyebrow]=\"resolvedEyebrow()\" [title]=\"resolvedTitle()\" [subtitle]=\"resolvedDescription()\">\n <div page-shell-header-meta class=\"property-offer-detail__header-meta\">\n <div class=\"property-offer-detail__page-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <div class=\"property-offer-detail__summary\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-offer-detail__summary-card\">\n <span class=\"property-offer-detail__summary-value\">{{ stat.value }}</span>\n <span class=\"property-offer-detail__summary-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-offer-detail__summary-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-page-collection-shell>\n }\n\n <ng-template #detailContent>\n @if (ctx.error()) {\n <section class=\"property-offer-detail__error\">{{ ctx.error() }}</section>\n }\n\n @if (ctx.offer()) {\n <div class=\"property-offer-detail__grid\">\n <div class=\"property-offer-detail__main\">\n <rolatech-offer-header-card></rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"property-offer-detail__side\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n } @else {\n <section class=\"property-offer-detail__loading\">Loading offer detail...</section>\n }\n </ng-template>\n</div>\n", styles: [":host{display:block}.property-offer-detail__workspace-actions{display:flex;justify-content:flex-end}.property-offer-detail__header-meta{display:flex;flex-direction:column;gap:.85rem}.property-offer-detail__page-actions{display:flex;justify-content:flex-end}.property-offer-detail__summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:.85rem}.property-offer-detail__summary-card{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-offer-detail__summary-label,.property-offer-detail__summary-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-offer-detail__grid{display:grid;gap:1rem}.property-offer-detail__main,.property-offer-detail__side{display:flex;flex-direction:column;gap:1rem}.property-offer-detail__error,.property-offer-detail__loading{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:var(--rt-base-background, #ffffff);padding:1rem 1.1rem;color:var(--rt-text-secondary)}.property-offer-detail__error{color:#b91c1c;background:color-mix(in srgb,#fff1f2 84%,var(--rt-base-background, #ffffff))}@media(min-width:1024px){.property-offer-detail__summary{grid-template-columns:repeat(4,minmax(0,1fr))}.property-offer-detail__grid{grid-template-columns:minmax(0,1.75fr) minmax(20rem,.9fr);align-items:start}}\n"] }]
4956
+ }], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], backLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "backLabel", required: false }] }] } });
4922
4957
 
4923
- class PropertyViewingDetailComponent extends BaseComponent {
4958
+ class ViewingDetailContext {
4924
4959
  propertyService = inject(PropertyService);
4925
- viewing;
4926
- applicantType = PropertyApplicantType;
4927
- async ngOnInit() {
4928
- this.route.params.subscribe((params) => {
4929
- const id = params['id'];
4930
- this.get(id);
4960
+ viewingId = signal(null, ...(ngDevMode ? [{ debugName: "viewingId" }] : []));
4961
+ viewing = signal(null, ...(ngDevMode ? [{ debugName: "viewing" }] : []));
4962
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
4963
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
4964
+ constructor() {
4965
+ effect(() => {
4966
+ const id = this.viewingId();
4967
+ if (!id) {
4968
+ return;
4969
+ }
4970
+ void this.reload(id);
4931
4971
  });
4932
4972
  }
4933
- get(id) {
4934
- this.propertyService.getViewing(id).subscribe({
4935
- next: (res) => {
4936
- this.viewing = res.data;
4937
- this.titleService.setTitle(this.viewing.item?.title || 'Viewing detail');
4938
- },
4973
+ init(id) {
4974
+ if (this.viewingId() !== id) {
4975
+ this.viewing.set(null);
4976
+ }
4977
+ this.error.set(null);
4978
+ this.viewingId.set(id);
4979
+ }
4980
+ clear() {
4981
+ this.viewingId.set(null);
4982
+ this.viewing.set(null);
4983
+ this.loading.set(false);
4984
+ this.error.set(null);
4985
+ }
4986
+ updateViewing(updater) {
4987
+ const current = this.viewing();
4988
+ if (!current) {
4989
+ return;
4990
+ }
4991
+ this.viewing.set(updater(current));
4992
+ }
4993
+ async reload(id = this.viewingId()) {
4994
+ if (!id) {
4995
+ return;
4996
+ }
4997
+ this.loading.set(true);
4998
+ this.error.set(null);
4999
+ try {
5000
+ const response = await firstValueFrom(this.propertyService.getViewing(id));
5001
+ if (this.viewingId() !== id) {
5002
+ return;
5003
+ }
5004
+ this.viewing.set(response.data ?? response);
5005
+ }
5006
+ catch (error) {
5007
+ if (this.viewingId() === id) {
5008
+ this.error.set(this.errorMessage(error, 'Failed to load viewing detail.'));
5009
+ }
5010
+ console.error(error);
5011
+ }
5012
+ finally {
5013
+ if (this.viewingId() === id) {
5014
+ this.loading.set(false);
5015
+ }
5016
+ }
5017
+ }
5018
+ errorMessage(error, fallback) {
5019
+ return error?.error?.message ?? error?.message ?? fallback;
5020
+ }
5021
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5022
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext });
5023
+ }
5024
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext, decorators: [{
5025
+ type: Injectable
5026
+ }], ctorParameters: () => [] });
5027
+
5028
+ class PropertyViewingFacade {
5029
+ propertyService = inject(PropertyService);
5030
+ snackBarService = inject(SnackBarService);
5031
+ ctx = inject(ViewingDetailContext);
5032
+ viewing = computed(() => this.ctx.viewing(), ...(ngDevMode ? [{ debugName: "viewing" }] : []));
5033
+ acceptingSlotId = signal(null, ...(ngDevMode ? [{ debugName: "acceptingSlotId" }] : []));
5034
+ cancelling = signal(false, ...(ngDevMode ? [{ debugName: "cancelling" }] : []));
5035
+ requiresSlotAcceptance = computed(() => {
5036
+ const viewing = this.viewing();
5037
+ return (!viewing?.viewingDate &&
5038
+ (viewing?.slotDecisionRequired === true ||
5039
+ this.slotProposalSource(viewing) === 'ADMIN_COUNTER' ||
5040
+ propertyViewingStatusCode(viewing?.status) === 'COUNTERED'));
5041
+ }, ...(ngDevMode ? [{ debugName: "requiresSlotAcceptance" }] : []));
5042
+ canCancel = computed(() => {
5043
+ const code = propertyViewingStatusCode(this.viewing()?.status);
5044
+ return code !== 'CANCELLED' && code !== 'COMPLETED';
5045
+ }, ...(ngDevMode ? [{ debugName: "canCancel" }] : []));
5046
+ canAcceptCounterSlot(slot) {
5047
+ return this.requiresSlotAcceptance() && !!slot?.id && this.acceptingSlotId() !== slot.id;
5048
+ }
5049
+ async cancel() {
5050
+ const viewingId = this.ctx.viewingId();
5051
+ if (!viewingId || !this.viewing() || !this.canCancel() || this.cancelling()) {
5052
+ return;
5053
+ }
5054
+ this.cancelling.set(true);
5055
+ try {
5056
+ await firstValueFrom(this.propertyService.cancelViewing(viewingId));
5057
+ this.ctx.updateViewing((viewing) => ({
5058
+ ...viewing,
5059
+ status: 'CANCELLED',
5060
+ slotDecisionRequired: false,
5061
+ }));
5062
+ this.snackBarService.open('Cancelled');
5063
+ }
5064
+ catch (error) {
5065
+ this.snackBarService.open(this.errorMessage(error, 'Failed to cancel viewing.'));
5066
+ }
5067
+ finally {
5068
+ this.cancelling.set(false);
5069
+ }
5070
+ }
5071
+ async acceptCounterSlot(slot) {
5072
+ const viewingId = this.ctx.viewingId();
5073
+ if (!viewingId) {
5074
+ return;
5075
+ }
5076
+ if (!slot?.id) {
5077
+ this.snackBarService.open('This slot cannot be accepted because the slot id is missing.');
5078
+ return;
5079
+ }
5080
+ this.acceptingSlotId.set(slot.id);
5081
+ try {
5082
+ const response = await firstValueFrom(this.propertyService.confirmViewing(viewingId, slot.id));
5083
+ const nextViewing = response?.data;
5084
+ this.ctx.updateViewing((viewing) => ({
5085
+ ...viewing,
5086
+ ...nextViewing,
5087
+ status: nextViewing?.status ?? 'APPROVED',
5088
+ viewingDate: nextViewing?.viewingDate ?? slot.date ?? viewing.viewingDate,
5089
+ viewingTime: nextViewing?.viewingTime ?? slot.time ?? viewing.viewingTime,
5090
+ proposedSlots: nextViewing?.proposedSlots ?? viewing.proposedSlots,
5091
+ item: nextViewing?.item ?? viewing.item,
5092
+ slotProposalSource: nextViewing?.slotProposalSource ?? viewing.slotProposalSource,
5093
+ slotDecisionRequired: nextViewing?.slotDecisionRequired ?? false,
5094
+ }));
5095
+ this.snackBarService.open('Viewing confirmed.');
5096
+ }
5097
+ catch (error) {
5098
+ this.snackBarService.open(this.errorMessage(error, 'Failed to confirm viewing.'));
5099
+ }
5100
+ finally {
5101
+ this.acceptingSlotId.set(null);
5102
+ }
5103
+ }
5104
+ slotProposalSource(viewing) {
5105
+ const explicitSource = viewing?.slotProposalSource;
5106
+ if (explicitSource) {
5107
+ return explicitSource.toUpperCase();
5108
+ }
5109
+ const slotSource = viewing?.proposedSlots?.[0]?.source;
5110
+ return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
5111
+ }
5112
+ errorMessage(error, fallback) {
5113
+ return error?.error?.message ?? error?.message ?? fallback;
5114
+ }
5115
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5116
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade });
5117
+ }
5118
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade, decorators: [{
5119
+ type: Injectable
5120
+ }] });
5121
+
5122
+ class PropertyViewingDetailComponent extends BaseComponent {
5123
+ ctx = inject(ViewingDetailContext);
5124
+ facade = inject(PropertyViewingFacade);
5125
+ viewing = computed(() => this.ctx.viewing(), ...(ngDevMode ? [{ debugName: "viewing" }] : []));
5126
+ applicantType = PropertyApplicantType;
5127
+ constructor() {
5128
+ super();
5129
+ effect(() => {
5130
+ this.titleService.setTitle(this.viewing()?.item?.title || 'Viewing detail');
4939
5131
  });
4940
5132
  }
4941
- cancel() {
4942
- this.propertyService.cancelViewing(this.id).subscribe({
4943
- next: (res) => {
4944
- this.viewing.status = 'CANCELLED';
4945
- this.snackBarService.open('Cancelled');
4946
- },
4947
- error: (error) => {
4948
- this.snackBarService.open(error.message);
4949
- },
5133
+ ngOnInit() {
5134
+ this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
5135
+ const id = params.get('id');
5136
+ if (!id) {
5137
+ this.ctx.clear();
5138
+ return;
5139
+ }
5140
+ this.ctx.init(id);
4950
5141
  });
4951
5142
  }
4952
5143
  statusLabel(status) {
@@ -4965,7 +5156,12 @@ class PropertyViewingDetailComponent extends BaseComponent {
4965
5156
  }
4966
5157
  }
4967
5158
  statusHeadline() {
4968
- switch (propertyViewingStatusCode(this.viewing?.status)) {
5159
+ if (this.facade.requiresSlotAcceptance()) {
5160
+ return 'The listing team returned new slots for you to choose from.';
5161
+ }
5162
+ switch (propertyViewingStatusCode(this.viewing()?.status)) {
5163
+ case 'COUNTERED':
5164
+ return 'The listing team returned new slots for you to choose from.';
4969
5165
  case 'APPROVED':
4970
5166
  return 'Your viewing is confirmed.';
4971
5167
  case 'COMPLETED':
@@ -4979,13 +5175,17 @@ class PropertyViewingDetailComponent extends BaseComponent {
4979
5175
  }
4980
5176
  }
4981
5177
  statusDescription() {
4982
- if (!this.viewing) {
5178
+ const viewing = this.viewing();
5179
+ if (!viewing) {
4983
5180
  return '';
4984
5181
  }
5182
+ if (this.facade.requiresSlotAcceptance()) {
5183
+ return 'Review the three returned options below and accept the slot that works best for you.';
5184
+ }
4985
5185
  if (this.confirmedSlotLabel()) {
4986
5186
  return `Confirmed for ${this.confirmedSlotLabel()}.`;
4987
5187
  }
4988
- switch (propertyViewingStatusCode(this.viewing.status)) {
5188
+ switch (propertyViewingStatusCode(viewing.status)) {
4989
5189
  case 'REJECTED':
4990
5190
  return 'Review the proposed slots and submit a new request if you still want to visit the property.';
4991
5191
  case 'CANCELLED':
@@ -4997,34 +5197,38 @@ class PropertyViewingDetailComponent extends BaseComponent {
4997
5197
  }
4998
5198
  }
4999
5199
  confirmedSlotLabel() {
5000
- if (!this.viewing?.viewingDate) {
5200
+ const viewing = this.viewing();
5201
+ if (!viewing?.viewingDate) {
5001
5202
  return null;
5002
5203
  }
5003
- const date = new Date(this.viewing.viewingDate);
5204
+ const date = new Date(viewing.viewingDate);
5004
5205
  const hasValidDate = !Number.isNaN(date.getTime());
5005
5206
  const formattedDate = hasValidDate
5006
5207
  ? new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium' }).format(date)
5007
- : this.viewing.viewingDate;
5008
- return this.viewing.viewingTime ? `${formattedDate} at ${this.viewing.viewingTime}` : formattedDate;
5208
+ : viewing.viewingDate;
5209
+ return viewing.viewingTime ? `${formattedDate} at ${viewing.viewingTime}` : formattedDate;
5009
5210
  }
5010
5211
  isConfirmedSlot(slot) {
5011
- return !!this.viewing?.viewingDate && slot.date === this.viewing.viewingDate && slot.time === this.viewing.viewingTime;
5212
+ const viewing = this.viewing();
5213
+ return !!viewing?.viewingDate && slot.date === viewing.viewingDate && slot.time === viewing.viewingTime;
5012
5214
  }
5013
5215
  primaryMediaUrl() {
5014
- return this.viewing?.item?.media?.[0]?.url || '';
5216
+ return this.viewing()?.item?.media?.[0]?.url || '';
5015
5217
  }
5016
5218
  requesterName() {
5017
- const fullName = `${this.viewing?.firstName ?? ''} ${this.viewing?.lastName ?? ''}`.trim();
5219
+ const viewing = this.viewing();
5220
+ const fullName = `${viewing?.firstName ?? ''} ${viewing?.lastName ?? ''}`.trim();
5018
5221
  return fullName || 'Viewer';
5019
5222
  }
5020
5223
  contactSummary() {
5021
- if (!this.viewing) {
5224
+ const viewing = this.viewing();
5225
+ if (!viewing) {
5022
5226
  return 'Contact details available below';
5023
5227
  }
5024
- if (this.viewing.email && this.viewing.phone) {
5025
- return `${this.viewing.email} · ${this.viewing.phone}`;
5228
+ if (viewing.email && viewing.phone) {
5229
+ return `${viewing.email} · ${viewing.phone}`;
5026
5230
  }
5027
- return this.viewing.email || this.viewing.phone || 'Contact details available below';
5231
+ return viewing.email || viewing.phone || 'Contact details available below';
5028
5232
  }
5029
5233
  viewerCategoryLabel(value) {
5030
5234
  if (!value) {
@@ -5046,10 +5250,6 @@ class PropertyViewingDetailComponent extends BaseComponent {
5046
5250
  requesterTypeLabel(value) {
5047
5251
  return value ? this.humanize(value) : 'Direct request';
5048
5252
  }
5049
- canCancel() {
5050
- const code = propertyViewingStatusCode(this.viewing?.status);
5051
- return code !== 'CANCELLED' && code !== 'COMPLETED';
5052
- }
5053
5253
  humanize(value) {
5054
5254
  return value
5055
5255
  .trim()
@@ -5057,8 +5257,8 @@ class PropertyViewingDetailComponent extends BaseComponent {
5057
5257
  .toLowerCase()
5058
5258
  .replace(/\b\w/g, (char) => char.toUpperCase());
5059
5259
  }
5060
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
5061
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Availability shared with the listing team</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\" i18n>\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
5260
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5261
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", providers: [ViewingDetailContext, PropertyViewingFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing() ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing() ? statusLabel(viewing()?.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing()?.item?.propertyId; as propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', propertyId]\" i18n>Open property</a>\n }\n @if (viewing() && facade.canCancel()) {\n <button mat-flat-button type=\"button\" [disabled]=\"facade.cancelling()\" (click)=\"facade.cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing(); as viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (facade.requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (facade.requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!facade.canAcceptCounterSlot(slot)\"\n (click)=\"facade.acceptCounterSlot(slot)\"\n >\n @if (facade.acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (facade.requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
5062
5262
  }
5063
5263
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, decorators: [{
5064
5264
  type: Component,
@@ -5072,8 +5272,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
5072
5272
  ToolbarComponent,
5073
5273
  RouterLink,
5074
5274
  PricePipe,
5075
- ], template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Availability shared with the listing team</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\" i18n>\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"] }]
5076
- }] });
5275
+ ], providers: [ViewingDetailContext, PropertyViewingFacade], template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing() ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing() ? statusLabel(viewing()?.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing()?.item?.propertyId; as propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', propertyId]\" i18n>Open property</a>\n }\n @if (viewing() && facade.canCancel()) {\n <button mat-flat-button type=\"button\" [disabled]=\"facade.cancelling()\" (click)=\"facade.cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing(); as viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (facade.requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (facade.requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!facade.canAcceptCounterSlot(slot)\"\n (click)=\"facade.acceptCounterSlot(slot)\"\n >\n @if (facade.acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (facade.requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"] }]
5276
+ }], ctorParameters: () => [] });
5077
5277
 
5078
5278
  class PropertyWishlistComponent extends BaseComponent {
5079
5279
  propertyService = inject(PropertyService);
@@ -6984,7 +7184,7 @@ class PropertyOfferCreate {
6984
7184
  }
6985
7185
  }
6986
7186
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, deps: [], target: i0.ɵɵFactoryTarget.Component });
6987
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Buyer workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "component", type: PropertyIntentShell, selector: "rolatech-property-intent-shell", inputs: ["property"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }] });
7187
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Market workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "component", type: PropertyIntentShell, selector: "rolatech-property-intent-shell", inputs: ["property"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }] });
6988
7188
  }
6989
7189
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, decorators: [{
6990
7190
  type: Component,
@@ -6995,7 +7195,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
6995
7195
  PropertyOfferSaleForm,
6996
7196
  PropertyIntentShell,
6997
7197
  PropertyMarketBreadcrumbComponent,
6998
- ], providers: [OfferFormFacade], template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Buyer workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"] }]
7198
+ ], providers: [OfferFormFacade], template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Market workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"] }]
6999
7199
  }], ctorParameters: () => [] });
7000
7200
 
7001
7201
  const propertyRoutes = [
@@ -7005,7 +7205,7 @@ const propertyRoutes = [
7005
7205
  children: [
7006
7206
  {
7007
7207
  path: '',
7008
- loadComponent: () => import('./rolatech-angular-property-property-index.component-De4homi3.mjs').then((x) => x.PropertyIndexComponent),
7208
+ loadComponent: () => import('./rolatech-angular-property-property-index.component-CLCJ78vg.mjs').then((x) => x.PropertyIndexComponent),
7009
7209
  },
7010
7210
  ],
7011
7211
  },
@@ -8860,6 +9060,9 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8860
9060
  propertyService = inject(PropertyService);
8861
9061
  authUserService = inject(AuthUserService);
8862
9062
  applicantType = PropertyApplicantType;
9063
+ counterMode = signal(false, ...(ngDevMode ? [{ debugName: "counterMode" }] : []));
9064
+ countering = signal(false, ...(ngDevMode ? [{ debugName: "countering" }] : []));
9065
+ counterSlots = signal(this.createCounterSlots(), ...(ngDevMode ? [{ debugName: "counterSlots" }] : []));
8863
9066
  viewing;
8864
9067
  agent;
8865
9068
  ngOnInit() {
@@ -8870,6 +9073,7 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8870
9073
  next: (res) => {
8871
9074
  this.viewing = res.data;
8872
9075
  this.titleService.setTitle(this.viewing.item?.title || 'Manage viewing request');
9076
+ this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
8873
9077
  if (this.viewing.agentId) {
8874
9078
  this.getAgentPublicInfo(this.viewing.agentId);
8875
9079
  }
@@ -8914,6 +9118,8 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8914
9118
  proposedSlots: res?.data?.proposedSlots ?? this.viewing.proposedSlots,
8915
9119
  item: res?.data?.item ?? this.viewing.item,
8916
9120
  };
9121
+ this.counterMode.set(false);
9122
+ this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
8917
9123
  this.snackBarService.open('Viewing confirmed');
8918
9124
  },
8919
9125
  error: (error) => {
@@ -8939,7 +9145,12 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8939
9145
  }
8940
9146
  }
8941
9147
  statusHeadline() {
9148
+ if (this.isAwaitingAcceptance()) {
9149
+ return 'Counter slots sent. Waiting for the requester to pick one.';
9150
+ }
8942
9151
  switch (propertyViewingStatusCode(this.viewing?.status)) {
9152
+ case 'COUNTERED':
9153
+ return 'Counter slots sent. Waiting for the requester to pick one.';
8943
9154
  case 'APPROVED':
8944
9155
  return 'Viewing confirmed with the applicant.';
8945
9156
  case 'COMPLETED':
@@ -8956,6 +9167,9 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8956
9167
  if (!this.viewing) {
8957
9168
  return '';
8958
9169
  }
9170
+ if (this.isAwaitingAcceptance()) {
9171
+ return 'The original requested times did not fit the diary, so three new options have been returned for the requester to accept.';
9172
+ }
8959
9173
  if (this.confirmedSlotLabel()) {
8960
9174
  return `The selected slot is ${this.confirmedSlotLabel()} and should now be visible across the viewing workflow.`;
8961
9175
  }
@@ -8984,7 +9198,81 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8984
9198
  return !!this.viewing?.viewingDate && slot.date === this.viewing.viewingDate && slot.time === this.viewing.viewingTime;
8985
9199
  }
8986
9200
  canConfirmSlot(slot) {
8987
- return !this.viewing?.viewingDate && !!slot?.id;
9201
+ return !this.viewing?.viewingDate && !!slot?.id && !this.isAwaitingAcceptance();
9202
+ }
9203
+ canCounterSlots() {
9204
+ const code = propertyViewingStatusCode(this.viewing?.status);
9205
+ return !this.viewing?.viewingDate && code !== 'COMPLETED' && code !== 'CANCELLED' && code !== 'REJECTED';
9206
+ }
9207
+ isAwaitingAcceptance() {
9208
+ return (!this.viewing?.viewingDate &&
9209
+ (this.viewing?.slotDecisionRequired === true ||
9210
+ this.slotProposalSource() === 'ADMIN_COUNTER' ||
9211
+ propertyViewingStatusCode(this.viewing?.status) === 'COUNTERED'));
9212
+ }
9213
+ openCounterSlots() {
9214
+ this.counterSlots.set(this.createCounterSlots(this.viewing?.proposedSlots));
9215
+ this.counterMode.set(true);
9216
+ }
9217
+ cancelCounterSlots() {
9218
+ this.counterMode.set(false);
9219
+ this.counterSlots.set(this.createCounterSlots(this.viewing?.proposedSlots));
9220
+ }
9221
+ updateCounterSlot(index, slot) {
9222
+ this.counterSlots.update((current) => current.map((item, itemIndex) => itemIndex === index
9223
+ ? {
9224
+ ...item,
9225
+ ...slot,
9226
+ }
9227
+ : item));
9228
+ }
9229
+ sendCounterSlots() {
9230
+ const counterSlots = this.counterSlots()
9231
+ .map((slot) => ({
9232
+ date: slot.date?.trim?.() ?? slot.date,
9233
+ time: slot.time?.trim?.() ?? slot.time,
9234
+ }))
9235
+ .filter((slot) => slot.date && slot.time);
9236
+ if (counterSlots.length !== 3) {
9237
+ this.snackBarService.open('Provide three counter slots before sending them to the requester.');
9238
+ return;
9239
+ }
9240
+ const uniqueSlots = new Set(counterSlots.map((slot) => `${slot.date}::${slot.time}`));
9241
+ if (uniqueSlots.size !== counterSlots.length) {
9242
+ this.snackBarService.open('Each counter slot must use a different date and time.');
9243
+ return;
9244
+ }
9245
+ this.countering.set(true);
9246
+ this.propertyService.counterViewing(this.id, { counterSlots }).subscribe({
9247
+ next: (res) => {
9248
+ const nextViewing = res?.data;
9249
+ this.viewing = {
9250
+ ...this.viewing,
9251
+ ...nextViewing,
9252
+ status: nextViewing?.status ?? 'COUNTERED',
9253
+ slotProposalSource: nextViewing?.slotProposalSource ?? 'ADMIN_COUNTER',
9254
+ slotDecisionRequired: nextViewing?.slotDecisionRequired ?? true,
9255
+ counteredAt: nextViewing?.counteredAt ?? new Date().toISOString(),
9256
+ proposedSlots: nextViewing?.proposedSlots ??
9257
+ counterSlots.map((slot, index) => ({
9258
+ ...slot,
9259
+ id: this.viewing.proposedSlots[index]?.id,
9260
+ source: 'ADMIN_COUNTER',
9261
+ })),
9262
+ item: nextViewing?.item ?? this.viewing.item,
9263
+ };
9264
+ this.counterMode.set(false);
9265
+ this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
9266
+ this.snackBarService.open('Counter slots sent to the requester.');
9267
+ },
9268
+ error: (error) => {
9269
+ this.countering.set(false);
9270
+ this.snackBarService.open(error.message);
9271
+ },
9272
+ complete: () => {
9273
+ this.countering.set(false);
9274
+ },
9275
+ });
8988
9276
  }
8989
9277
  primaryMediaUrl() {
8990
9278
  return this.viewing?.item?.media?.[0]?.url || '';
@@ -9041,8 +9329,24 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
9041
9329
  .toLowerCase()
9042
9330
  .replace(/\b\w/g, (char) => char.toUpperCase());
9043
9331
  }
9332
+ slotProposalSource() {
9333
+ const explicitSource = this.viewing?.slotProposalSource;
9334
+ if (explicitSource) {
9335
+ return explicitSource.toUpperCase();
9336
+ }
9337
+ const slotSource = this.viewing?.proposedSlots?.[0]?.source;
9338
+ return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
9339
+ }
9340
+ createCounterSlots(source = []) {
9341
+ return Array.from({ length: 3 }, (_item, index) => ({
9342
+ date: source[index]?.date ?? '',
9343
+ time: source[index]?.time ?? '',
9344
+ source: source[index]?.source ?? null,
9345
+ state: source[index]?.state ?? null,
9346
+ }));
9347
+ }
9044
9348
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyManageViewingsDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
9045
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyManageViewingsDetailComponent, isStandalone: true, selector: "rolatech-property-manage-viewings-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Choose the slot to confirm</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\" i18n>\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
9349
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyManageViewingsDetailComponent, isStandalone: true, selector: "rolatech-property-manage-viewings-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\">\n @if (isAwaitingAcceptance()) {\n Waiting for requester acceptance\n } @else {\n Choose the slot to confirm\n }\n </h2>\n </div>\n\n @if (canCounterSlots()) {\n <button mat-stroked-button type=\"button\" (click)=\"openCounterSlots()\" i18n>Counter with 3 new slots</button>\n }\n </div>\n\n @if (counterMode()) {\n <div class=\"property-manage-viewings-detail__counter-panel\">\n <div class=\"property-manage-viewings-detail__counter-copy\">\n <strong i18n>Return three new slots</strong>\n <p i18n>\n Use this when the original request does not fit the diary. The requester will need to accept one of these\n returned options.\n </p>\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-grid\">\n @for (slot of counterSlots(); track $index) {\n <div class=\"property-manage-viewings-detail__counter-card\">\n <div class=\"property-manage-viewings-detail__counter-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time\n [proposedTime]=\"slot\"\n (output)=\"updateCounterSlot($index, $event)\"\n ></rolatech-property-viewing-time>\n </div>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"cancelCounterSlots()\" i18n>Cancel</button>\n <button mat-flat-button type=\"button\" [disabled]=\"countering()\" (click)=\"sendCounterSlots()\">\n @if (countering()) {\n Sending...\n } @else {\n Send counter slots\n }\n </button>\n </div>\n </div>\n }\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else if (isAwaitingAcceptance()) {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Sent for acceptance</span>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\">\n @if (isAwaitingAcceptance()) {\n The requester now needs to accept one of the returned slots. Once they do, the accepted time becomes the source\n of truth for this viewing.\n } @else {\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n }\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__counter-panel{display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem;padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-brand-color) 6%,var(--rt-raised-background, #ffffff) 94%)}.property-manage-viewings-detail__counter-copy{display:flex;flex-direction:column;gap:.35rem}.property-manage-viewings-detail__counter-copy p{margin:0;color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__counter-grid{display:grid;gap:.85rem}.property-manage-viewings-detail__counter-card{padding:.95rem 1rem .35rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-manage-viewings-detail__counter-label{margin-bottom:.5rem;color:var(--rt-text-secondary);font-size:.82rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.property-manage-viewings-detail__counter-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.75rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__counter-actions{justify-content:stretch}.property-manage-viewings-detail__counter-actions>*{flex:1 1 100%}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
9046
9350
  }
9047
9351
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyManageViewingsDetailComponent, decorators: [{
9048
9352
  type: Component,
@@ -9055,13 +9359,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9055
9359
  Skeleton,
9056
9360
  ToolbarComponent,
9057
9361
  PricePipe,
9058
- ], template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Choose the slot to confirm</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\" i18n>\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"] }]
9362
+ FormsModule,
9363
+ PropertyViewingTimeComponent,
9364
+ ], template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\">\n @if (isAwaitingAcceptance()) {\n Waiting for requester acceptance\n } @else {\n Choose the slot to confirm\n }\n </h2>\n </div>\n\n @if (canCounterSlots()) {\n <button mat-stroked-button type=\"button\" (click)=\"openCounterSlots()\" i18n>Counter with 3 new slots</button>\n }\n </div>\n\n @if (counterMode()) {\n <div class=\"property-manage-viewings-detail__counter-panel\">\n <div class=\"property-manage-viewings-detail__counter-copy\">\n <strong i18n>Return three new slots</strong>\n <p i18n>\n Use this when the original request does not fit the diary. The requester will need to accept one of these\n returned options.\n </p>\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-grid\">\n @for (slot of counterSlots(); track $index) {\n <div class=\"property-manage-viewings-detail__counter-card\">\n <div class=\"property-manage-viewings-detail__counter-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time\n [proposedTime]=\"slot\"\n (output)=\"updateCounterSlot($index, $event)\"\n ></rolatech-property-viewing-time>\n </div>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"cancelCounterSlots()\" i18n>Cancel</button>\n <button mat-flat-button type=\"button\" [disabled]=\"countering()\" (click)=\"sendCounterSlots()\">\n @if (countering()) {\n Sending...\n } @else {\n Send counter slots\n }\n </button>\n </div>\n </div>\n }\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else if (isAwaitingAcceptance()) {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Sent for acceptance</span>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\">\n @if (isAwaitingAcceptance()) {\n The requester now needs to accept one of the returned slots. Once they do, the accepted time becomes the source\n of truth for this viewing.\n } @else {\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n }\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__counter-panel{display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem;padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-brand-color) 6%,var(--rt-raised-background, #ffffff) 94%)}.property-manage-viewings-detail__counter-copy{display:flex;flex-direction:column;gap:.35rem}.property-manage-viewings-detail__counter-copy p{margin:0;color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__counter-grid{display:grid;gap:.85rem}.property-manage-viewings-detail__counter-card{padding:.95rem 1rem .35rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-manage-viewings-detail__counter-label{margin-bottom:.5rem;color:var(--rt-text-secondary);font-size:.82rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.property-manage-viewings-detail__counter-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.75rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__counter-actions{justify-content:stretch}.property-manage-viewings-detail__counter-actions>*{flex:1 1 100%}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"] }]
9059
9365
  }] });
9060
9366
 
9061
9367
  const propertyManageViewingsRoutes = [
9062
9368
  {
9063
9369
  path: '',
9064
- loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-C37LmOC0.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
9370
+ loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-Dx9AXFNI.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
9065
9371
  },
9066
9372
  {
9067
9373
  path: ':id',
@@ -9110,19 +9416,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9110
9416
  args: ['class.rolatech-property-market-item']
9111
9417
  }], letting: [{ type: i0.Input, args: [{ isSignal: true, alias: "letting", required: true }] }], thumbnail: [{ type: i0.Input, args: [{ isSignal: true, alias: "thumbnail", required: false }] }], list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: false }] }] } });
9112
9418
 
9113
- class PropertyWorkspaceShell {
9114
- eyebrow = input('Workspace', ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
9115
- title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
9116
- description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
9117
- stats = input([], ...(ngDevMode ? [{ debugName: "stats" }] : []));
9118
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
9119
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyWorkspaceShell, isStandalone: true, selector: "rolatech-property-workspace-shell", inputs: { eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, stats: { classPropertyName: "stats", publicName: "stats", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
9120
- }
9121
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, decorators: [{
9122
- type: Component,
9123
- args: [{ selector: 'rolatech-property-workspace-shell', imports: [CommonModule], template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"] }]
9124
- }], propDecorators: { eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], stats: [{ type: i0.Input, args: [{ isSignal: true, alias: "stats", required: false }] }] } });
9125
-
9126
9419
  class PropertyMarketIndex extends BaseComponent {
9127
9420
  propertySearchService = inject(PropertySearchService);
9128
9421
  loading = false;
@@ -9597,10 +9890,22 @@ class PropertyMarketViewingIndex extends BaseComponent {
9597
9890
  stats = computed(() => {
9598
9891
  const viewings = this.viewings();
9599
9892
  return [
9600
- { label: 'Buyer requests', value: this.length || viewings.length, hint: 'Visits requested while you are acting as a buyer' },
9601
- { label: 'Pending', value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'PENDING').length, hint: 'Waiting for listing-side review' },
9602
- { label: 'Approved', value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'APPROVED').length, hint: 'Confirmed appointments in your buyer workspace' },
9603
- { label: 'Completed', value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'COMPLETED').length, hint: 'Finished visits linked to saved market activity' },
9893
+ { label: 'Requests', value: this.length || viewings.length, hint: 'Visits requested while you are acting as a buyer' },
9894
+ {
9895
+ label: 'Pending',
9896
+ value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'PENDING').length,
9897
+ hint: 'Waiting for listing-side review',
9898
+ },
9899
+ {
9900
+ label: 'Approved',
9901
+ value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'APPROVED').length,
9902
+ hint: 'Confirmed appointments in your buyer workspace',
9903
+ },
9904
+ {
9905
+ label: 'Completed',
9906
+ value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'COMPLETED').length,
9907
+ hint: 'Finished visits linked to saved market activity',
9908
+ },
9604
9909
  ];
9605
9910
  }, ...(ngDevMode ? [{ debugName: "stats" }] : []));
9606
9911
  ngOnInit() {
@@ -9649,7 +9954,7 @@ class PropertyMarketViewingIndex extends BaseComponent {
9649
9954
  });
9650
9955
  }
9651
9956
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
9652
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingIndex, isStandalone: true, selector: "rolatech-property-market-viewing-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"Buyer viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"Buyer viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) {\n @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n }\n } @else if (viewings().length) {\n @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }] });
9957
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingIndex, isStandalone: true, selector: "rolatech-property-market-viewing-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"workspace\"\n title=\"viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n } } @else if (viewings().length) { @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }] });
9653
9958
  }
9654
9959
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, decorators: [{
9655
9960
  type: Component,
@@ -9660,7 +9965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9660
9965
  PropertyViewingItemComponent,
9661
9966
  MatPaginatorModule,
9662
9967
  PropertyWorkspaceShell,
9663
- ], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"Buyer viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"Buyer viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) {\n @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n }\n } @else if (viewings().length) {\n @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
9968
+ ], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"workspace\"\n title=\"viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n } } @else if (viewings().length) { @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
9664
9969
  }] });
9665
9970
 
9666
9971
  class PropertyMarketViewingDetail {
@@ -9679,10 +9984,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9679
9984
 
9680
9985
  class PropertyMarketOfferIndex extends BaseComponent {
9681
9986
  propertyOfferService = inject(PropertyOfferService);
9682
- breadcrumbs = [
9683
- { label: 'Properties', link: '/properties' },
9684
- { label: 'Offers' },
9685
- ];
9987
+ breadcrumbs = [{ label: 'Properties', link: '/properties' }, { label: 'Offers' }];
9686
9988
  select = 0;
9687
9989
  links = [
9688
9990
  { name: 'All', status: '' },
@@ -9701,10 +10003,22 @@ class PropertyMarketOfferIndex extends BaseComponent {
9701
10003
  stats = computed(() => {
9702
10004
  const offers = this.offers();
9703
10005
  return [
9704
- { label: 'Buyer offers', value: this.length || offers.length, hint: 'Offers you submitted while acting as a buyer agent' },
9705
- { label: 'Submitted', value: offers.filter((item) => item.status === PropertyOfferStatus.SUBMITTED).length, hint: 'Waiting for listing-side review or response' },
9706
- { label: 'Accepted', value: offers.filter((item) => item.status === PropertyOfferStatus.ACCEPTED).length, hint: 'Offers moved forward in the negotiation flow' },
9707
- { label: 'Cancelled', value: offers.filter((item) => item.status === PropertyOfferStatus.CANCELLED).length, hint: 'Closed or withdrawn buyer negotiations' },
10006
+ { label: 'Offers', value: this.length || offers.length, hint: 'Offers you submitted while acting as a buyer agent' },
10007
+ {
10008
+ label: 'Submitted',
10009
+ value: offers.filter((item) => item.status === PropertyOfferStatus.SUBMITTED).length,
10010
+ hint: 'Waiting for listing-side review or response',
10011
+ },
10012
+ {
10013
+ label: 'Accepted',
10014
+ value: offers.filter((item) => item.status === PropertyOfferStatus.ACCEPTED).length,
10015
+ hint: 'Offers moved forward in the negotiation flow',
10016
+ },
10017
+ {
10018
+ label: 'Cancelled',
10019
+ value: offers.filter((item) => item.status === PropertyOfferStatus.CANCELLED).length,
10020
+ hint: 'Closed or withdrawn buyer negotiations',
10021
+ },
9708
10022
  ];
9709
10023
  }, ...(ngDevMode ? [{ debugName: "stats" }] : []));
9710
10024
  ngOnInit() {
@@ -9753,7 +10067,7 @@ class PropertyMarketOfferIndex extends BaseComponent {
9753
10067
  });
9754
10068
  }
9755
10069
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
9756
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferIndex, isStandalone: true, selector: "rolatech-property-market-offer-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"Buyer offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) {\n @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n }\n } @else if (offers().length > 0) {\n @for (item of offers(); track item.id) {\n <rolatech-property-offer-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [offer]=\"item\"></rolatech-property-offer-item>\n }\n } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferItemComponent, selector: "rolatech-property-offer-item", inputs: ["offer"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: OfferItemSkeleton, selector: "rolatech-offer-item-skeleton" }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }], encapsulation: i0.ViewEncapsulation.None });
10070
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferIndex, isStandalone: true, selector: "rolatech-property-market-offer-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n } } @else if (offers().length > 0) { @for (item of offers(); track item.id) {\n <rolatech-property-offer-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [offer]=\"item\"\n ></rolatech-property-offer-item>\n } } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferItemComponent, selector: "rolatech-property-offer-item", inputs: ["offer"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: OfferItemSkeleton, selector: "rolatech-offer-item-skeleton" }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }], encapsulation: i0.ViewEncapsulation.None });
9757
10071
  }
9758
10072
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, decorators: [{
9759
10073
  type: Component,
@@ -9765,7 +10079,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9765
10079
  MatPaginatorModule,
9766
10080
  OfferItemSkeleton,
9767
10081
  PropertyWorkspaceShell,
9768
- ], encapsulation: ViewEncapsulation.None, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"Buyer offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) {\n @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n }\n } @else if (offers().length > 0) {\n @for (item of offers(); track item.id) {\n <rolatech-property-offer-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [offer]=\"item\"></rolatech-property-offer-item>\n }\n } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
10082
+ ], encapsulation: ViewEncapsulation.None, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n } } @else if (offers().length > 0) { @for (item of offers(); track item.id) {\n <rolatech-property-offer-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [offer]=\"item\"\n ></rolatech-property-offer-item>\n } } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
9769
10083
  }] });
9770
10084
 
9771
10085
  class PropertyMarketOfferDetail {
@@ -9775,11 +10089,11 @@ class PropertyMarketOfferDetail {
9775
10089
  { label: 'Offer detail' },
9776
10090
  ];
9777
10091
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
9778
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyMarketOfferDetail, isStandalone: true, selector: "rolatech-property-market-offer-detail", ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferDetailComponent, selector: "rolatech-property-offer-detail" }] });
10092
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyMarketOfferDetail, isStandalone: true, selector: "rolatech-property-market-offer-detail", ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail\n [mode]=\"'agent'\"\n eyebrow=\"Market workflow\"\n title=\"Offer detail\"\n description=\"Track buyer-side negotiations, compare counter-offers, and keep every response in the same market workspace.\"\n backLabel=\"Back to buyer offers\"\n ></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferDetailComponent, selector: "rolatech-property-offer-detail", inputs: ["mode", "eyebrow", "title", "description", "backLabel"] }] });
9779
10093
  }
9780
10094
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, decorators: [{
9781
10095
  type: Component,
9782
- args: [{ selector: 'rolatech-property-market-offer-detail', imports: [CommonModule, PropertyMarketBreadcrumbComponent, PropertyOfferDetailComponent], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"] }]
10096
+ args: [{ selector: 'rolatech-property-market-offer-detail', imports: [CommonModule, PropertyMarketBreadcrumbComponent, PropertyOfferDetailComponent], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail\n [mode]=\"'agent'\"\n eyebrow=\"Market workflow\"\n title=\"Offer detail\"\n description=\"Track buyer-side negotiations, compare counter-offers, and keep every response in the same market workspace.\"\n backLabel=\"Back to buyer offers\"\n ></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"] }]
9783
10097
  }] });
9784
10098
 
9785
10099
  class PropertyMarketViewingRequest extends BaseComponent {
@@ -9872,7 +10186,7 @@ class PropertyMarketViewingRequest extends BaseComponent {
9872
10186
  this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
9873
10187
  }
9874
10188
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
9875
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingRequest, isStandalone: true, selector: "rolatech-letting-viewing-request", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three\n viewing options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__slot-section\">\n <div class=\"property-market-viewing-request__slot-head\">\n <h3 i18n>Preferred viewing slots</h3>\n <p i18n>Choose three options across at least two different days so the seller can confirm quickly.</p>\n </div>\n\n <div class=\"property-market-viewing-request__slots\">\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"property-market-viewing-request__slot-card\">\n <div class=\"property-market-viewing-request__slot-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n </div>\n }\n </div>\n </div>\n\n <button mat-flat-button class=\"property-market-viewing-request__submit\" (click)=\"sendRequest()\" [disabled]=\"sending\">\n <span class=\"property-market-viewing-request__submit-content\">\n @if (sending) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span i18n>{{ sending ? 'Sending request...' : 'Send request' }}</span>\n </span>\n </button>\n </section>\n </main>\n\n <aside class=\"property-market-viewing-request__side\">\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <div class=\"property-market-viewing-request__summary-head\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-viewing-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>What happens next</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Updates appear in your viewings inbox</h2>\n <p class=\"property-market-viewing-request__description\" i18n>\n Once admin confirms a slot, you will see the approved time in the viewing detail page and can jump back to this\n property from the breadcrumb trail.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-viewing-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-viewing-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-viewing-request__hero,.property-market-viewing-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-viewing-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__eyebrow,.property-market-viewing-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-viewing-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-viewing-request__description,.property-market-viewing-request__hint,.property-market-viewing-request__slot-head p{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-viewing-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-viewing-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-viewing-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-viewing-request__fact-label,.property-market-viewing-request__slot-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-viewing-request__content,.property-market-viewing-request__main,.property-market-viewing-request__side,.property-market-viewing-request__slots{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-request__panel{padding:1.25rem}.property-market-viewing-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__panel-head,.property-market-viewing-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-viewing-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-viewing-request__grid{display:grid;gap:.85rem}.property-market-viewing-request__slot-section{display:flex;flex-direction:column;gap:1rem;margin-top:.5rem}.property-market-viewing-request__slot-head h3{margin:0 0 .3rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-request__slot-card{display:flex;flex-direction:column;gap:.6rem;padding:1rem;border-radius:1.15rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-additive-background, var(--rt-base-background, #ffffff)) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-request__submit{min-height:3rem;border-radius:9999px}.property-market-viewing-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-viewing-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-viewing-request{padding:1.25rem}.property-market-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1100px){.property-market-viewing-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output", "select"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
10189
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingRequest, isStandalone: true, selector: "rolatech-letting-viewing-request", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three viewing\n options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\"> {{ applicantType.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__slot-section\">\n <div class=\"property-market-viewing-request__slot-head\">\n <h3 i18n>Preferred viewing slots</h3>\n <p i18n>Choose three options across at least two different days so the seller can confirm quickly.</p>\n </div>\n\n <div class=\"property-market-viewing-request__slots\">\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"property-market-viewing-request__slot-card\">\n <div class=\"property-market-viewing-request__slot-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n </div>\n }\n </div>\n </div>\n\n <button mat-flat-button class=\"property-market-viewing-request__submit\" (click)=\"sendRequest()\" [disabled]=\"sending\">\n <span class=\"property-market-viewing-request__submit-content\">\n @if (sending) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span i18n>{{ sending ? 'Sending request...' : 'Send request' }}</span>\n </span>\n </button>\n </section>\n </main>\n\n <aside class=\"property-market-viewing-request__side\">\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <div class=\"property-market-viewing-request__summary-head\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-viewing-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>What happens next</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Updates appear in your viewings inbox</h2>\n <p class=\"property-market-viewing-request__description\" i18n>\n Once admin confirms a slot, you will see the approved time in the viewing detail page and can jump back to this\n property from the breadcrumb trail.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-viewing-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-viewing-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-viewing-request__hero,.property-market-viewing-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-viewing-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__eyebrow,.property-market-viewing-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-viewing-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-viewing-request__description,.property-market-viewing-request__hint,.property-market-viewing-request__slot-head p{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-viewing-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-viewing-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-viewing-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-viewing-request__fact-label,.property-market-viewing-request__slot-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-viewing-request__content,.property-market-viewing-request__main,.property-market-viewing-request__side,.property-market-viewing-request__slots{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-request__panel{padding:1.25rem}.property-market-viewing-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__panel-head,.property-market-viewing-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-viewing-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-viewing-request__grid{display:grid;gap:.85rem}.property-market-viewing-request__slot-section{display:flex;flex-direction:column;gap:1rem;margin-top:.5rem}.property-market-viewing-request__slot-head h3{margin:0 0 .3rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-request__slot-card{display:flex;flex-direction:column;gap:.6rem;padding:1rem;border-radius:1.15rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-additive-background, var(--rt-base-background, #ffffff)) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-request__submit{min-height:3rem;border-radius:9999px}.property-market-viewing-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-viewing-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-viewing-request{padding:1.25rem}.property-market-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1100px){.property-market-viewing-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
9876
10190
  }
9877
10191
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, decorators: [{
9878
10192
  type: Component,
@@ -9891,7 +10205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9891
10205
  PropertyPricingComponent,
9892
10206
  PropertyMarketBreadcrumbComponent,
9893
10207
  MatSelectModule,
9894
- ], template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three\n viewing options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__slot-section\">\n <div class=\"property-market-viewing-request__slot-head\">\n <h3 i18n>Preferred viewing slots</h3>\n <p i18n>Choose three options across at least two different days so the seller can confirm quickly.</p>\n </div>\n\n <div class=\"property-market-viewing-request__slots\">\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"property-market-viewing-request__slot-card\">\n <div class=\"property-market-viewing-request__slot-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n </div>\n }\n </div>\n </div>\n\n <button mat-flat-button class=\"property-market-viewing-request__submit\" (click)=\"sendRequest()\" [disabled]=\"sending\">\n <span class=\"property-market-viewing-request__submit-content\">\n @if (sending) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span i18n>{{ sending ? 'Sending request...' : 'Send request' }}</span>\n </span>\n </button>\n </section>\n </main>\n\n <aside class=\"property-market-viewing-request__side\">\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <div class=\"property-market-viewing-request__summary-head\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-viewing-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>What happens next</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Updates appear in your viewings inbox</h2>\n <p class=\"property-market-viewing-request__description\" i18n>\n Once admin confirms a slot, you will see the approved time in the viewing detail page and can jump back to this\n property from the breadcrumb trail.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-viewing-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-viewing-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-viewing-request__hero,.property-market-viewing-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-viewing-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__eyebrow,.property-market-viewing-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-viewing-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-viewing-request__description,.property-market-viewing-request__hint,.property-market-viewing-request__slot-head p{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-viewing-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-viewing-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-viewing-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-viewing-request__fact-label,.property-market-viewing-request__slot-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-viewing-request__content,.property-market-viewing-request__main,.property-market-viewing-request__side,.property-market-viewing-request__slots{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-request__panel{padding:1.25rem}.property-market-viewing-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__panel-head,.property-market-viewing-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-viewing-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-viewing-request__grid{display:grid;gap:.85rem}.property-market-viewing-request__slot-section{display:flex;flex-direction:column;gap:1rem;margin-top:.5rem}.property-market-viewing-request__slot-head h3{margin:0 0 .3rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-request__slot-card{display:flex;flex-direction:column;gap:.6rem;padding:1rem;border-radius:1.15rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-additive-background, var(--rt-base-background, #ffffff)) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-request__submit{min-height:3rem;border-radius:9999px}.property-market-viewing-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-viewing-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-viewing-request{padding:1.25rem}.property-market-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1100px){.property-market-viewing-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"] }]
10208
+ ], template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three viewing\n options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\"> {{ applicantType.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__slot-section\">\n <div class=\"property-market-viewing-request__slot-head\">\n <h3 i18n>Preferred viewing slots</h3>\n <p i18n>Choose three options across at least two different days so the seller can confirm quickly.</p>\n </div>\n\n <div class=\"property-market-viewing-request__slots\">\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"property-market-viewing-request__slot-card\">\n <div class=\"property-market-viewing-request__slot-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n </div>\n }\n </div>\n </div>\n\n <button mat-flat-button class=\"property-market-viewing-request__submit\" (click)=\"sendRequest()\" [disabled]=\"sending\">\n <span class=\"property-market-viewing-request__submit-content\">\n @if (sending) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span i18n>{{ sending ? 'Sending request...' : 'Send request' }}</span>\n </span>\n </button>\n </section>\n </main>\n\n <aside class=\"property-market-viewing-request__side\">\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <div class=\"property-market-viewing-request__summary-head\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-viewing-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>What happens next</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Updates appear in your viewings inbox</h2>\n <p class=\"property-market-viewing-request__description\" i18n>\n Once admin confirms a slot, you will see the approved time in the viewing detail page and can jump back to this\n property from the breadcrumb trail.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-viewing-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-viewing-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-viewing-request__hero,.property-market-viewing-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-viewing-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__eyebrow,.property-market-viewing-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-viewing-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-viewing-request__description,.property-market-viewing-request__hint,.property-market-viewing-request__slot-head p{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-viewing-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-viewing-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-viewing-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-viewing-request__fact-label,.property-market-viewing-request__slot-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-viewing-request__content,.property-market-viewing-request__main,.property-market-viewing-request__side,.property-market-viewing-request__slots{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-request__panel{padding:1.25rem}.property-market-viewing-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__panel-head,.property-market-viewing-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-viewing-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-viewing-request__grid{display:grid;gap:.85rem}.property-market-viewing-request__slot-section{display:flex;flex-direction:column;gap:1rem;margin-top:.5rem}.property-market-viewing-request__slot-head h3{margin:0 0 .3rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-request__slot-card{display:flex;flex-direction:column;gap:.6rem;padding:1rem;border-radius:1.15rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-additive-background, var(--rt-base-background, #ffffff)) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-request__submit{min-height:3rem;border-radius:9999px}.property-market-viewing-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-viewing-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-viewing-request{padding:1.25rem}.property-market-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1100px){.property-market-viewing-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"] }]
9895
10209
  }], ctorParameters: () => [] });
9896
10210
 
9897
10211
  class PropertyMarketOfferRequest extends BaseComponent {
@@ -9989,7 +10303,7 @@ class PropertyMarketOfferRequest extends BaseComponent {
9989
10303
  }
9990
10304
  }
9991
10305
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
9992
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferRequest, isStandalone: true, selector: "rolatech-property-market-offer-request", providers: [OfferFormFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review,\n approval, or counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs\n that take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
10306
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferRequest, isStandalone: true, selector: "rolatech-property-market-offer-request", providers: [OfferFormFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review, approval, or\n counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs that\n take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
9993
10307
  }
9994
10308
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, decorators: [{
9995
10309
  type: Component,
@@ -10002,7 +10316,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
10002
10316
  PropertyMarketBreadcrumbComponent,
10003
10317
  PropertyOfferRentalForm,
10004
10318
  PropertyOfferSaleForm,
10005
- ], providers: [OfferFormFacade], template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review,\n approval, or counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs\n that take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"] }]
10319
+ ], providers: [OfferFormFacade], template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review, approval, or\n counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs that\n take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"] }]
10006
10320
  }], ctorParameters: () => [] });
10007
10321
 
10008
10322
  class PropertyMarketSavedIndex {
@@ -10360,72 +10674,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
10360
10674
  ], template: "<rolatech-property-workspace-shell\n eyebrow=\"Agent workspace\"\n title=\"Listing viewings\"\n description=\"Keep buyer appointments organized across your managed listings, review the latest requests, and move straight into viewing detail when something changes.\"\n [stats]=\"stats()\"\n>\n <nav workspace-filters class=\"property-listing-viewing-index__status-nav\" aria-label=\"Listing viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-listing-viewing-index__status-link\"\n [class.property-listing-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-listing-viewing-index__list\">\n @if (loading) {\n @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-listing-viewing-index__placeholder\"></div>\n }\n } @else if (viewings().length) {\n @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [viewing]=\"item\"></rolatech-property-viewing-item>\n }\n } @else {\n <div class=\"property-listing-viewing-index__empty\">\n <h3 i18n>No viewing activity yet</h3>\n <p i18n>Incoming listing appointments will appear here once buyers or partner agents send requests.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</rolatech-property-workspace-shell>\n", styles: [":host{display:block;padding:1rem}.property-listing-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-listing-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.property-listing-viewing-index__status-link:hover,.property-listing-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-listing-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-listing-viewing-index__placeholder,.property-listing-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-listing-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-listing-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-listing-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){:host{padding:1.25rem}}\n"] }]
10361
10675
  }] });
10362
10676
 
10363
- class PropertyListingViewingDetail extends BaseComponent {
10364
- propertyService = inject(PropertyService);
10365
- authUserService = inject(AuthUserService);
10366
- viewing;
10367
- name = '';
10368
- agent;
10369
- viewingTimeConfirmed = false;
10370
- selectedSlotId = '';
10371
- ngOnInit() {
10372
- this.getViewing();
10373
- }
10374
- getViewing() {
10375
- this.propertyService.getViewing(this.id).subscribe({
10376
- next: (res) => {
10377
- this.viewing = res.data;
10378
- this.name = this.viewing.firstName + ', ' + this.viewing.lastName;
10379
- if (this.viewing.agentId) {
10380
- this.getAgentPublicInfo(this.viewing.agentId);
10381
- }
10382
- this.viewingTimeConfirmed = this.viewing.viewingDate ? true : false;
10383
- },
10384
- });
10385
- }
10386
- getAgentPublicInfo(agentId) {
10387
- this.authUserService.getPublicUserInfo(agentId).subscribe({
10388
- next: (res) => {
10389
- this.agent = res;
10390
- },
10391
- });
10392
- }
10393
- findConfirmedViewingTime() {
10394
- const viewingProposedSlot = this.viewing.proposedSlots.find((item) => {
10395
- return item.date === this.viewing.viewingDate && item.time === this.viewing.viewingTime;
10396
- });
10397
- this.selectedSlotId = viewingProposedSlot.id;
10398
- }
10399
- confirmViewingTime(item) {
10400
- const options = {
10401
- title: 'Confirm viewing time',
10402
- component: PropertyViewingConfirmationComponent,
10403
- data: {
10404
- proposedTime: item,
10405
- },
10406
- };
10407
- this.dialogService.open(options);
10408
- this.dialogService.confirmed().subscribe({
10409
- next: (result) => {
10410
- if (result) {
10411
- this.propertyService.confirmViewing(this.id, item.id).subscribe({
10412
- next: (res) => {
10413
- this.viewingTimeConfirmed = true;
10414
- },
10415
- });
10416
- }
10417
- },
10418
- });
10419
- }
10420
- statusLabel(status) {
10421
- return propertyViewingStatusLabel(status);
10422
- }
10423
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, deps: null, target: i0.ɵɵFactoryTarget.Component });
10424
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingViewingDetail, isStandalone: true, selector: "rolatech-property-listing-viewing-detail", usesInheritance: true, ngImport: i0, template: "@if (viewing) {\n<rolatech-toolbar [title]=\"statusLabel(viewing.status)\" large link=\"../\"></rolatech-toolbar>\n<div class=\"px-4\">\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewer</div>\n <hr class=\"mb-2\" />\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"name\"></rolatech-rich-label>\n </div>\n </div>\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) { @if (item.date === viewing.viewingDate && item.time ===\n viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n } } @if (!viewingTimeConfirmed) {\n <div class=\"ml-3\"><button mat-flat-button (click)=\"confirmViewingTime(item)\" i18n>Confirm</button></div>\n }\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n @if (viewing.item) {\n <div class=\"flex items-center py-2\">\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @if (viewing.item.media[0]?.url) {\n @defer {\n <rolatech-thumbnail [src]=\"viewing.item.media[0]?.url || ''\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n } @else {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing.item.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewing agent</div>\n <hr class=\"mb-2\" />\n @if (agent) {\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"agent.name\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Email\" [title]=\"agent.email\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Phone\" [title]=\"agent.phone\"></rolatech-rich-label>\n </div>\n }\n </div>\n</div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: RichLabelComponent, selector: "rolatech-rich-label", inputs: ["label", "title"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
10677
+ class PropertyListingViewingDetail {
10678
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
10679
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyListingViewingDetail, isStandalone: true, selector: "rolatech-property-listing-viewing-detail", ngImport: i0, template: "<rolatech-property-manage-viewings-detail></rolatech-property-manage-viewings-detail>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyManageViewingsDetailComponent, selector: "rolatech-property-manage-viewings-detail" }] });
10425
10680
  }
10426
10681
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, decorators: [{
10427
10682
  type: Component,
10428
- args: [{ selector: 'rolatech-property-listing-viewing-detail', imports: [CommonModule, ToolbarComponent, RichLabelComponent, ThumbnailComponent, MatButtonModule, PricePipe], template: "@if (viewing) {\n<rolatech-toolbar [title]=\"statusLabel(viewing.status)\" large link=\"../\"></rolatech-toolbar>\n<div class=\"px-4\">\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewer</div>\n <hr class=\"mb-2\" />\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"name\"></rolatech-rich-label>\n </div>\n </div>\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) { @if (item.date === viewing.viewingDate && item.time ===\n viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n } } @if (!viewingTimeConfirmed) {\n <div class=\"ml-3\"><button mat-flat-button (click)=\"confirmViewingTime(item)\" i18n>Confirm</button></div>\n }\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n @if (viewing.item) {\n <div class=\"flex items-center py-2\">\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @if (viewing.item.media[0]?.url) {\n @defer {\n <rolatech-thumbnail [src]=\"viewing.item.media[0]?.url || ''\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n } @else {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing.item.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewing agent</div>\n <hr class=\"mb-2\" />\n @if (agent) {\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"agent.name\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Email\" [title]=\"agent.email\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Phone\" [title]=\"agent.phone\"></rolatech-rich-label>\n </div>\n }\n </div>\n</div>\n}\n" }]
10683
+ args: [{ selector: 'rolatech-property-listing-viewing-detail', imports: [CommonModule, PropertyManageViewingsDetailComponent], template: "<rolatech-property-manage-viewings-detail></rolatech-property-manage-viewings-detail>\n" }]
10429
10684
  }] });
10430
10685
 
10431
10686
  const propertyListingViewingRoutes = [
@@ -10475,7 +10730,7 @@ class PropertyListingOfferDetail extends BaseComponent {
10475
10730
  this.propertyOfferService.getOffer(this.id).subscribe({
10476
10731
  next: (res) => {
10477
10732
  this.offer = res.data;
10478
- // this.name = this.isRental()? this.offer.rentalTerms?.tenancyLengthMonths
10733
+ this.name = this.applicantName();
10479
10734
  this.getProperty(this.offer.propertyId);
10480
10735
  },
10481
10736
  });
@@ -10560,7 +10815,9 @@ class PropertyListingOfferDetail extends BaseComponent {
10560
10815
  }
10561
10816
  });
10562
10817
  }
10563
- counter() { }
10818
+ counter() {
10819
+ this.f.enterEdit();
10820
+ }
10564
10821
  rfPassed() {
10565
10822
  const dialogRef = this.dialog.open(AcceptDialogComponent, {
10566
10823
  width: '400px',
@@ -10671,19 +10928,58 @@ class PropertyListingOfferDetail extends BaseComponent {
10671
10928
  },
10672
10929
  });
10673
10930
  }
10931
+ applicantName() {
10932
+ const primaryTenant = this.offer?.tenants?.[0];
10933
+ return primaryTenant?.fullName || this.offer?.sale?.buyerName || this.name || 'Applicant';
10934
+ }
10935
+ applicantContactSummary() {
10936
+ if (!this.offer) {
10937
+ return 'Contact details appear here once the offer loads.';
10938
+ }
10939
+ const email = this.applicantEmail();
10940
+ const phone = this.applicantPhone();
10941
+ if (email && phone) {
10942
+ return `${email} · ${phone}`;
10943
+ }
10944
+ return email || phone || 'Contact details not shared';
10945
+ }
10946
+ agentName() {
10947
+ return this.agent?.name || 'Assigned agent';
10948
+ }
10949
+ agentEmail() {
10950
+ return this.agent?.email || 'Not shared';
10951
+ }
10952
+ agentPhone() {
10953
+ return this.agent?.phone || 'Not shared';
10954
+ }
10955
+ propertyMediaUrl() {
10956
+ return this.property?.media?.[0]?.url || '';
10957
+ }
10958
+ applicantEmail() {
10959
+ return this.offer?.tenants?.[0]?.email || this.offer?.sale?.email || '';
10960
+ }
10961
+ applicantPhone() {
10962
+ return this.offer?.tenants?.[0]?.phone || this.offer?.sale?.phone || '';
10963
+ }
10964
+ targetDate() {
10965
+ return this.offer?.rentalTerms?.moveInDate || this.offer?.sale?.proposedExchangeDate || null;
10966
+ }
10674
10967
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingOfferDetail, deps: null, target: i0.ɵɵFactoryTarget.Component });
10675
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingOfferDetail, isStandalone: true, selector: "rolatech-property-listing-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], usesInheritance: true, ngImport: i0, template: "@if (offer) {\n<rolatech-toolbar [title]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4 h-fit\">\n <!-- Primary negotiation actions -->\n <rolatech-offer-negotiation-section />\n\n <rolatech-offer-item-card />\n\n <!-- Tenants card -->\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-(--rt-border-color) bg-(--rt-surface-2) p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <!-- Offer references -->\n <rolatech-offer-reference-provider />\n\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">Counter Offer</button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n</mat-menu>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferReferenceProvider, selector: "rolatech-offer-reference-provider", inputs: ["provider", "providerOther"], outputs: ["providerChange", "providerOtherChange"] }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferInternalNoteCard, selector: "rolatech-offer-internal-note-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }] });
10968
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingOfferDetail, isStandalone: true, selector: "rolatech-property-listing-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], usesInheritance: true, ngImport: i0, template: "@if (offer) {\n<div class=\"property-listing-offer-detail\">\n <rolatech-toolbar [title]=\"'Offer detail'\" [subtitle]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button type=\"button\" (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <section class=\"property-listing-offer-detail__hero\">\n <div class=\"property-listing-offer-detail__hero-copy\">\n <span class=\"property-listing-offer-detail__eyebrow\" i18n>Agent workspace</span>\n\n <div class=\"property-listing-offer-detail__hero-heading\">\n <h1 class=\"property-listing-offer-detail__title\">{{ property.title || 'Offer detail' }}</h1>\n <span class=\"property-listing-offer-detail__status\">{{ statusLabel() }}</span>\n </div>\n\n <p class=\"property-listing-offer-detail__intro\" i18n>\n Keep negotiation, billing, referencing, and applicant coordination aligned from one offer workspace.\n </p>\n\n <div class=\"property-listing-offer-detail__fact-grid\">\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Offer amount</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.item.amount | price }}</strong>\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ targetDate() ? (targetDate() | date: 'mediumDate') : 'Not shared' }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Invoice option</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ invoiceOptionLabel[invoiceOptionDraft() || offer.invoiceOption] }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Applicants</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.tenants?.length ?? 1 }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__hero-side\">\n <article class=\"property-listing-offer-detail__property-card\">\n <div class=\"property-listing-offer-detail__media\">\n @if (propertyMediaUrl()) { @defer {\n <rolatech-thumbnail [src]=\"propertyMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-listing-offer-detail__media-fallback\"></div>\n } } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-listing-offer-detail__property-copy\">\n <div class=\"property-listing-offer-detail__property-price\">{{ (property.price || offer.item.amount) | price }}</div>\n @if (property) {\n <div class=\"property-listing-offer-detail__property-facts\">\n <span>{{ property.bedrooms }} bed</span>\n <span>{{ property.bathrooms }} bath</span>\n <span>{{ property.receptions }} reception</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"property-listing-offer-detail__summary-card\">\n <span class=\"property-listing-offer-detail__summary-label\" i18n>Applicant</span>\n <strong class=\"property-listing-offer-detail__summary-value\">{{ applicantName() }}</strong>\n <span class=\"property-listing-offer-detail__summary-contact\">{{ applicantContactSummary() }}</span>\n </article>\n </div>\n </section>\n\n <div class=\"property-listing-offer-detail__content\">\n <main class=\"property-listing-offer-detail__main\">\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </main>\n\n <aside class=\"property-listing-offer-detail__side\">\n <div class=\"property-listing-offer-detail__sticky\">\n <section class=\"property-listing-offer-detail__panel\">\n <div class=\"property-listing-offer-detail__panel-head\">\n <div>\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Billing</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move-in invoice handling</h2>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <p class=\"property-listing-offer-detail__summary-note\" i18n>\n Choose whether security deposit and first rent land on a combined invoice or separate invoices.\n </p>\n\n <div class=\"property-listing-offer-detail__radio-grid\">\n <mat-radio-group\n class=\"block\"\n [value]=\"invoiceOptionDraft() || offer.invoiceOption\"\n (change)=\"setInvoiceOption($event.value)\"\n >\n <div class=\"grid gap-3\">\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with security deposit and first rent on separate lines.\n </div>\n </div>\n </div>\n </label>\n\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Issue separate invoices so move-in payments can be tracked independently.\n </div>\n </div>\n </div>\n </label>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <rolatech-offer-reference-provider />\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Applicant contact</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ applicantName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ applicantEmail() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ applicantPhone() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>User id</span>\n <strong>{{ offer.userId || 'Not attached' }}</strong>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__action-row\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantEmail()\" (click)=\"emailApplicant(applicantEmail())\">\n Email\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantPhone()\" (click)=\"callApplicant(applicantPhone())\">\n Call\n </button>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Listing agent</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ agentName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Agent id</span>\n <strong>{{ offer.agentId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Quick actions</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move the negotiation forward</h2>\n\n <div class=\"property-listing-offer-detail__stack-actions\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button type=\"button\" (click)=\"rfPassed()\">References passed</button>\n <button mat-stroked-button type=\"button\" (click)=\"rfFailed()\">References failed</button>\n }\n </div>\n </section>\n </div>\n </aside>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">\n Counter Offer\n </button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n </mat-menu>\n</div>\n}\n", styles: [":host{display:block}.property-listing-offer-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%),var(--rt-base-background, #ffffff))}.property-listing-offer-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-listing-offer-detail__hero-copy,.property-listing-offer-detail__hero-side,.property-listing-offer-detail__main,.property-listing-offer-detail__side,.property-listing-offer-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-listing-offer-detail__eyebrow,.property-listing-offer-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-listing-offer-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-listing-offer-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-listing-offer-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.82rem;font-weight:700}.property-listing-offer-detail__intro,.property-listing-offer-detail__summary-note,.property-listing-offer-detail__summary-contact{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-listing-offer-detail__intro{color:var(--rt-text-primary);font-size:1.02rem;font-weight:700}.property-listing-offer-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-listing-offer-detail__fact,.property-listing-offer-detail__summary-card,.property-listing-offer-detail__property-card,.property-listing-offer-detail__panel,.property-listing-offer-detail__radio-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-offer-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-listing-offer-detail__fact-label,.property-listing-offer-detail__summary-label,.property-listing-offer-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-listing-offer-detail__fact-value,.property-listing-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-listing-offer-detail__property-card{display:flex;flex-direction:column;gap:.95rem;padding:1rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-listing-offer-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-listing-offer-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-listing-offer-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-listing-offer-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-listing-offer-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-listing-offer-detail__summary-card,.property-listing-offer-detail__panel{display:flex;flex-direction:column;gap:.4rem;padding:1rem 1.05rem}.property-listing-offer-detail__summary-value,.property-listing-offer-detail__summary-contact,.property-listing-offer-detail__summary-list strong{overflow-wrap:anywhere;word-break:break-word}.property-listing-offer-detail__content{display:grid;gap:1rem}.property-listing-offer-detail__panel{border-radius:1.5rem;padding:1.25rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-listing-offer-detail__panel-head{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-listing-offer-detail__radio-grid{margin-top:.5rem}.property-listing-offer-detail__radio-card{display:block;padding:.95rem 1rem}.property-listing-offer-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-listing-offer-detail__action-row,.property-listing-offer-detail__stack-actions{display:grid;gap:.75rem}@media(min-width:640px){.property-listing-offer-detail__action-row{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:768px){.property-listing-offer-detail{padding:1.25rem}.property-listing-offer-detail__hero{grid-template-columns:minmax(0,1.5fr) minmax(18rem,.9fr);align-items:stretch;padding:1.5rem}.property-listing-offer-detail__hero-heading,.property-listing-offer-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-listing-offer-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-listing-offer-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-listing-offer-detail__fact-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferReferenceProvider, selector: "rolatech-offer-reference-provider", inputs: ["provider", "providerOther"], outputs: ["providerChange", "providerOtherChange"] }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferInternalNoteCard, selector: "rolatech-offer-internal-note-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
10676
10969
  }
10677
10970
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingOfferDetail, decorators: [{
10678
10971
  type: Component,
10679
10972
  args: [{ selector: 'rolatech-property-listing-offer-detail', imports: [
10680
10973
  CommonModule,
10681
10974
  ToolbarComponent,
10975
+ ThumbnailComponent,
10976
+ ImagePlaceholderComponent,
10682
10977
  MatButtonModule,
10683
10978
  MatIcon,
10684
10979
  MatMenuModule,
10685
10980
  MatRadioModule,
10686
10981
  FormsModule,
10982
+ PricePipe,
10687
10983
  OfferRentalTermsCard,
10688
10984
  OfferTenantsAccordion,
10689
10985
  OfferItemCard,
@@ -10694,7 +10990,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
10694
10990
  OfferCounterLatestCard,
10695
10991
  OfferCounterPreviousCard,
10696
10992
  OfferNegotiationSection,
10697
- ], providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], template: "@if (offer) {\n<rolatech-toolbar [title]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4 h-fit\">\n <!-- Primary negotiation actions -->\n <rolatech-offer-negotiation-section />\n\n <rolatech-offer-item-card />\n\n <!-- Tenants card -->\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-(--rt-border-color) bg-(--rt-surface-2) p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <!-- Offer references -->\n <rolatech-offer-reference-provider />\n\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">Counter Offer</button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n</mat-menu>\n}\n" }]
10993
+ ], providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], template: "@if (offer) {\n<div class=\"property-listing-offer-detail\">\n <rolatech-toolbar [title]=\"'Offer detail'\" [subtitle]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button type=\"button\" (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <section class=\"property-listing-offer-detail__hero\">\n <div class=\"property-listing-offer-detail__hero-copy\">\n <span class=\"property-listing-offer-detail__eyebrow\" i18n>Agent workspace</span>\n\n <div class=\"property-listing-offer-detail__hero-heading\">\n <h1 class=\"property-listing-offer-detail__title\">{{ property.title || 'Offer detail' }}</h1>\n <span class=\"property-listing-offer-detail__status\">{{ statusLabel() }}</span>\n </div>\n\n <p class=\"property-listing-offer-detail__intro\" i18n>\n Keep negotiation, billing, referencing, and applicant coordination aligned from one offer workspace.\n </p>\n\n <div class=\"property-listing-offer-detail__fact-grid\">\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Offer amount</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.item.amount | price }}</strong>\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ targetDate() ? (targetDate() | date: 'mediumDate') : 'Not shared' }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Invoice option</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ invoiceOptionLabel[invoiceOptionDraft() || offer.invoiceOption] }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Applicants</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.tenants?.length ?? 1 }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__hero-side\">\n <article class=\"property-listing-offer-detail__property-card\">\n <div class=\"property-listing-offer-detail__media\">\n @if (propertyMediaUrl()) { @defer {\n <rolatech-thumbnail [src]=\"propertyMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-listing-offer-detail__media-fallback\"></div>\n } } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-listing-offer-detail__property-copy\">\n <div class=\"property-listing-offer-detail__property-price\">{{ (property.price || offer.item.amount) | price }}</div>\n @if (property) {\n <div class=\"property-listing-offer-detail__property-facts\">\n <span>{{ property.bedrooms }} bed</span>\n <span>{{ property.bathrooms }} bath</span>\n <span>{{ property.receptions }} reception</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"property-listing-offer-detail__summary-card\">\n <span class=\"property-listing-offer-detail__summary-label\" i18n>Applicant</span>\n <strong class=\"property-listing-offer-detail__summary-value\">{{ applicantName() }}</strong>\n <span class=\"property-listing-offer-detail__summary-contact\">{{ applicantContactSummary() }}</span>\n </article>\n </div>\n </section>\n\n <div class=\"property-listing-offer-detail__content\">\n <main class=\"property-listing-offer-detail__main\">\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </main>\n\n <aside class=\"property-listing-offer-detail__side\">\n <div class=\"property-listing-offer-detail__sticky\">\n <section class=\"property-listing-offer-detail__panel\">\n <div class=\"property-listing-offer-detail__panel-head\">\n <div>\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Billing</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move-in invoice handling</h2>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <p class=\"property-listing-offer-detail__summary-note\" i18n>\n Choose whether security deposit and first rent land on a combined invoice or separate invoices.\n </p>\n\n <div class=\"property-listing-offer-detail__radio-grid\">\n <mat-radio-group\n class=\"block\"\n [value]=\"invoiceOptionDraft() || offer.invoiceOption\"\n (change)=\"setInvoiceOption($event.value)\"\n >\n <div class=\"grid gap-3\">\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with security deposit and first rent on separate lines.\n </div>\n </div>\n </div>\n </label>\n\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Issue separate invoices so move-in payments can be tracked independently.\n </div>\n </div>\n </div>\n </label>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <rolatech-offer-reference-provider />\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Applicant contact</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ applicantName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ applicantEmail() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ applicantPhone() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>User id</span>\n <strong>{{ offer.userId || 'Not attached' }}</strong>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__action-row\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantEmail()\" (click)=\"emailApplicant(applicantEmail())\">\n Email\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantPhone()\" (click)=\"callApplicant(applicantPhone())\">\n Call\n </button>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Listing agent</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ agentName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Agent id</span>\n <strong>{{ offer.agentId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Quick actions</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move the negotiation forward</h2>\n\n <div class=\"property-listing-offer-detail__stack-actions\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button type=\"button\" (click)=\"rfPassed()\">References passed</button>\n <button mat-stroked-button type=\"button\" (click)=\"rfFailed()\">References failed</button>\n }\n </div>\n </section>\n </div>\n </aside>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">\n Counter Offer\n </button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n </mat-menu>\n</div>\n}\n", styles: [":host{display:block}.property-listing-offer-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%),var(--rt-base-background, #ffffff))}.property-listing-offer-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-listing-offer-detail__hero-copy,.property-listing-offer-detail__hero-side,.property-listing-offer-detail__main,.property-listing-offer-detail__side,.property-listing-offer-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-listing-offer-detail__eyebrow,.property-listing-offer-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-listing-offer-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-listing-offer-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-listing-offer-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.82rem;font-weight:700}.property-listing-offer-detail__intro,.property-listing-offer-detail__summary-note,.property-listing-offer-detail__summary-contact{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-listing-offer-detail__intro{color:var(--rt-text-primary);font-size:1.02rem;font-weight:700}.property-listing-offer-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-listing-offer-detail__fact,.property-listing-offer-detail__summary-card,.property-listing-offer-detail__property-card,.property-listing-offer-detail__panel,.property-listing-offer-detail__radio-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-offer-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-listing-offer-detail__fact-label,.property-listing-offer-detail__summary-label,.property-listing-offer-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-listing-offer-detail__fact-value,.property-listing-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-listing-offer-detail__property-card{display:flex;flex-direction:column;gap:.95rem;padding:1rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-listing-offer-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-listing-offer-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-listing-offer-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-listing-offer-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-listing-offer-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-listing-offer-detail__summary-card,.property-listing-offer-detail__panel{display:flex;flex-direction:column;gap:.4rem;padding:1rem 1.05rem}.property-listing-offer-detail__summary-value,.property-listing-offer-detail__summary-contact,.property-listing-offer-detail__summary-list strong{overflow-wrap:anywhere;word-break:break-word}.property-listing-offer-detail__content{display:grid;gap:1rem}.property-listing-offer-detail__panel{border-radius:1.5rem;padding:1.25rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-listing-offer-detail__panel-head{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-listing-offer-detail__radio-grid{margin-top:.5rem}.property-listing-offer-detail__radio-card{display:block;padding:.95rem 1rem}.property-listing-offer-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-listing-offer-detail__action-row,.property-listing-offer-detail__stack-actions{display:grid;gap:.75rem}@media(min-width:640px){.property-listing-offer-detail__action-row{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:768px){.property-listing-offer-detail{padding:1.25rem}.property-listing-offer-detail__hero{grid-template-columns:minmax(0,1.5fr) minmax(18rem,.9fr);align-items:stretch;padding:1.5rem}.property-listing-offer-detail__hero-heading,.property-listing-offer-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-listing-offer-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-listing-offer-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-listing-offer-detail__fact-grid{grid-template-columns:1fr}}\n"] }]
10698
10994
  }] });
10699
10995
 
10700
10996
  class PropertyListingOfferIndex extends BaseComponent {
@@ -11359,4 +11655,4 @@ function provideAngularPropertyFeature() {
11359
11655
  */
11360
11656
 
11361
11657
  export { PropertyScope as A, PropertyViewingStatus as B, PropertyViewerCategory as C, propertyViewingStatusCode as D, propertyViewingStatusLabel as E, PropertyOfferTimelineStatus as F, PropertyOfferType as G, PropertyOfferStatus as H, EmploymentStatus as I, ResidencyStatus as J, PropertyApplicantType as K, AdverseCreditStatus as L, Market as M, BedroomRange as N, PriceRange as O, PropertyUtil as P, RentFrequency as R, ViewingTime as V, PropertyViewType as a, PropertyManageItemSkeleton as b, PropertyViewingItemComponent as c, propertyManageOffersRoutes as d, propertyRoutes as e, featureManageRoutes as f, propertyManageRoutes as g, propertyManageViewingsRoutes as h, propertyMarketRoutes as i, propertyListingViewingRoutes as j, propertyListingOfferRoutes as k, propertyAgentOverviewRoutes as l, propertyManageOverviewRoutes as m, propertyListingRoutes as n, provideAngularProperty as o, propertyReviewRoutes as p, provideAngularPropertyFeature as q, PropertyActionsComponent as r, PropertyItemComponent as s, tenantManageRoutes as t, PropertyPricingComponent as u, PropertyPriceType as v, PropertyVideoProvider as w, PropertyStatus as x, PropertyType as y, PropertyInventoryStatus as z };
11362
- //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-DSfebWFu.mjs.map
11658
+ //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-zL_Uw36W.mjs.map