@rolatech/angular-property 20.3.2-beta.0 → 20.3.2-beta.2

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';
@@ -1138,7 +1139,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
1138
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" }]
1139
1140
  }] });
1140
1141
 
1141
- const MY_FORMATS$8 = {
1142
+ const MY_FORMATS$7 = {
1142
1143
  parse: {
1143
1144
  dateInput: 'YYYY-MM-DD',
1144
1145
  },
@@ -1163,7 +1164,7 @@ class OfferEdit extends BaseComponent {
1163
1164
  useClass: MomentDateAdapter,
1164
1165
  deps: [MAT_DATE_LOCALE],
1165
1166
  },
1166
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$8 },
1167
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
1167
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 }] });
1168
1169
  }
1169
1170
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferEdit, decorators: [{
@@ -1174,7 +1175,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
1174
1175
  useClass: MomentDateAdapter,
1175
1176
  deps: [MAT_DATE_LOCALE],
1176
1177
  },
1177
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$8 },
1178
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
1178
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"] }]
1179
1180
  }], propDecorators: { offer: [{ type: i0.Input, args: [{ isSignal: true, alias: "offer", required: true }] }, { type: i0.Output, args: ["offerChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
1180
1181
 
@@ -1336,12 +1337,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
1336
1337
 
1337
1338
  class OfferRentalTermsCard {
1338
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
+ }
1339
1356
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1340
- 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 });
1341
1358
  }
1342
1359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, decorators: [{
1343
1360
  type: Component,
1344
- 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"] }]
1345
1362
  }] });
1346
1363
 
1347
1364
  class OfferTenantExpanded {
@@ -1413,11 +1430,11 @@ class OfferTenantCard {
1413
1430
  this.onToggleInput()(this.index());
1414
1431
  }
1415
1432
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1416
- 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"] }] });
1417
1434
  }
1418
1435
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, decorators: [{
1419
1436
  type: Component,
1420
- 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"] }]
1421
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 }] }] } });
1422
1439
 
1423
1440
  class OfferTenantsAccordion {
@@ -1449,11 +1466,11 @@ class OfferTenantsAccordion {
1449
1466
  this.expandedIndexes.set(new Set());
1450
1467
  }
1451
1468
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
1452
- 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 });
1453
1470
  }
1454
1471
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, decorators: [{
1455
1472
  type: Component,
1456
- 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"] }]
1457
1474
  }] });
1458
1475
 
1459
1476
  class OfferItemCard {
@@ -1463,21 +1480,31 @@ class OfferItemCard {
1463
1480
  return m?.url ?? null;
1464
1481
  }
1465
1482
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1466
- 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 });
1467
1484
  }
1468
1485
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, decorators: [{
1469
1486
  type: Component,
1470
- 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"] }]
1471
1488
  }] });
1472
1489
 
1473
1490
  class OfferSaleDetailsCard {
1474
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
+ }
1475
1502
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1476
- 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" }] });
1477
1504
  }
1478
1505
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, decorators: [{
1479
1506
  type: Component,
1480
- 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"] }]
1481
1508
  }] });
1482
1509
 
1483
1510
  // offer-manage.facade.ts
@@ -1879,11 +1906,11 @@ class OfferHistoryCard {
1879
1906
  return `${this.actorLabel(h.actor)} • ${this.actionLabel(h.action)}`;
1880
1907
  }
1881
1908
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1882
- 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 });
1883
1910
  }
1884
1911
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, decorators: [{
1885
1912
  type: Component,
1886
- 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"] }]
1887
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 }] }] } });
1888
1915
 
1889
1916
  class OfferHistoryAccordion {
@@ -1927,14 +1954,14 @@ class OfferHistoryAccordion {
1927
1954
  }
1928
1955
  }
1929
1956
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
1930
- 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 });
1931
1958
  }
1932
1959
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, decorators: [{
1933
1960
  type: Component,
1934
- 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"] }]
1935
1962
  }] });
1936
1963
 
1937
- const MY_FORMATS$7 = {
1964
+ const MY_FORMATS$6 = {
1938
1965
  parse: {
1939
1966
  dateInput: 'YYYY-MM-DD',
1940
1967
  },
@@ -2016,7 +2043,7 @@ class OfferCounterDialog {
2016
2043
  useClass: MomentDateAdapter,
2017
2044
  deps: [MAT_DATE_LOCALE],
2018
2045
  },
2019
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
2046
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
2020
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"] }] });
2021
2048
  }
2022
2049
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterDialog, decorators: [{
@@ -2038,7 +2065,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
2038
2065
  useClass: MomentDateAdapter,
2039
2066
  deps: [MAT_DATE_LOCALE],
2040
2067
  },
2041
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
2068
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
2042
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" }]
2043
2070
  }], propDecorators: { term: [{ type: i0.Input, args: [{ isSignal: true, alias: "term", required: true }] }, { type: i0.Output, args: ["termChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
2044
2071
 
@@ -2861,22 +2888,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
2861
2888
 
2862
2889
  class OfferCounterLatestCard {
2863
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
+ }
2864
2907
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
2865
- 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 });
2866
2909
  }
2867
2910
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, decorators: [{
2868
2911
  type: Component,
2869
- 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"] }]
2870
2913
  }] });
2871
2914
 
2872
2915
  class OfferCounterPreviousCard {
2873
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
+ }
2874
2933
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
2875
- 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 });
2876
2935
  }
2877
2936
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, decorators: [{
2878
2937
  type: Component,
2879
- 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"] }]
2880
2939
  }] });
2881
2940
 
2882
2941
  class OfferNegotiationSection {
@@ -2912,11 +2971,11 @@ class OfferNegotiationSection {
2912
2971
  });
2913
2972
  }
2914
2973
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
2915
- 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 });
2916
2975
  }
2917
2976
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, decorators: [{
2918
2977
  type: Component,
2919
- 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"] }]
2920
2979
  }] });
2921
2980
 
2922
2981
  class PropertyManageOfferDetailComponent extends BaseComponent {
@@ -3278,7 +3337,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
3278
3337
  }]
3279
3338
  }] });
3280
3339
 
3281
- const MY_FORMATS$6 = {
3340
+ const MY_FORMATS$5 = {
3282
3341
  parse: {
3283
3342
  dateInput: 'YYYY-MM-DD',
3284
3343
  },
@@ -3394,7 +3453,7 @@ class PropertyFilterBar {
3394
3453
  useClass: MomentDateAdapter,
3395
3454
  deps: [MAT_DATE_LOCALE],
3396
3455
  },
3397
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
3456
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
3398
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 });
3399
3458
  }
3400
3459
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyFilterBar, decorators: [{
@@ -3414,7 +3473,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
3414
3473
  useClass: MomentDateAdapter,
3415
3474
  deps: [MAT_DATE_LOCALE],
3416
3475
  },
3417
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
3476
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
3418
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"] }]
3419
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"] }] } });
3420
3479
 
@@ -4087,7 +4146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4087
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" }]
4088
4147
  }], ctorParameters: () => [] });
4089
4148
 
4090
- const MY_FORMATS$5 = {
4149
+ const MY_FORMATS$4 = {
4091
4150
  parse: {
4092
4151
  dateInput: 'YYYY-MM-DD',
4093
4152
  },
@@ -4159,7 +4218,7 @@ class PropertyViewingTimeComponent {
4159
4218
  useClass: MomentDateAdapter,
4160
4219
  deps: [MAT_DATE_LOCALE],
4161
4220
  },
4162
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
4221
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
4163
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 }] });
4164
4223
  }
4165
4224
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, decorators: [{
@@ -4179,7 +4238,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4179
4238
  useClass: MomentDateAdapter,
4180
4239
  deps: [MAT_DATE_LOCALE],
4181
4240
  },
4182
- { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
4241
+ { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
4183
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"] }]
4184
4243
  }], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
4185
4244
 
@@ -4304,7 +4363,7 @@ class PropertyViewingRequestComponent extends BaseComponent {
4304
4363
  this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
4305
4364
  }
4306
4365
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4307
- 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"] }, { 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" }] });
4308
4367
  }
4309
4368
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, decorators: [{
4310
4369
  type: Component,
@@ -4322,7 +4381,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4322
4381
  PropertyViewingTimeComponent,
4323
4382
  PropertyIntentShell,
4324
4383
  PropertyMarketBreadcrumbComponent,
4325
- ], 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"] }]
4326
4385
  }], ctorParameters: () => [] });
4327
4386
 
4328
4387
  class PropertyOfferIndexComponent extends BaseComponent {
@@ -4571,6 +4630,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4571
4630
 
4572
4631
  class OfferHeaderCard {
4573
4632
  ctx = inject(OfferDetailContext);
4633
+ offerStatusLabel = offerStatusLabel;
4574
4634
  ok = new Set([
4575
4635
  PropertyOfferStatus$1.ACCEPTED,
4576
4636
  PropertyOfferStatus$1.REFERENCES_PASSED,
@@ -4605,15 +4665,15 @@ class OfferHeaderCard {
4605
4665
  ]);
4606
4666
  statusBadgeClass(status) {
4607
4667
  if (!status)
4608
- return 'bg-gray-50 text-gray-700 border-gray-200';
4668
+ return 'offer-header-card__status offer-header-card__status--neutral';
4609
4669
  if (this.ok.has(status))
4610
- return 'bg-green-50 text-green-700 border-green-200';
4670
+ return 'offer-header-card__status offer-header-card__status--positive';
4611
4671
  if (this.warn.has(status))
4612
- return 'bg-yellow-50 text-yellow-800 border-yellow-200';
4672
+ return 'offer-header-card__status offer-header-card__status--warning';
4613
4673
  if (this.bad.has(status))
4614
- return 'bg-red-50 text-red-700 border-red-200';
4674
+ return 'offer-header-card__status offer-header-card__status--negative';
4615
4675
  // DRAFT or unknown
4616
- return 'bg-gray-50 text-gray-700 border-gray-200';
4676
+ return 'offer-header-card__status offer-header-card__status--neutral';
4617
4677
  }
4618
4678
  statusHint(status) {
4619
4679
  if (!status)
@@ -4673,270 +4733,215 @@ class OfferHeaderCard {
4673
4733
  return '';
4674
4734
  }
4675
4735
  }
4736
+ typeLabel(value) {
4737
+ if (!value) {
4738
+ return 'Offer';
4739
+ }
4740
+ return `${String(value).toLowerCase().replace(/^\w/, (char) => char.toUpperCase())} offer`;
4741
+ }
4676
4742
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
4677
- 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"] }] });
4678
4744
  }
4679
4745
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, decorators: [{
4680
4746
  type: Component,
4681
- 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"] }]
4682
4748
  }] });
4683
4749
 
4684
4750
  class OfferSideSummaryCard {
4685
4751
  ctx = inject(OfferDetailContext);
4752
+ statusLabel = offerStatusLabel;
4686
4753
  salePaymentMethodLabel = {
4687
4754
  CASH: 'Cash',
4688
4755
  MORTGAGE: 'Mortgage',
4689
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
+ }
4690
4808
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
4691
- 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" }] });
4692
4810
  }
4693
4811
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, decorators: [{
4694
4812
  type: Component,
4695
- 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"] }]
4696
4814
  }] });
4697
4815
 
4698
- const MY_FORMATS$4 = {
4699
- parse: {
4700
- dateInput: 'YYYY-MM-DD',
4701
- },
4702
- display: {
4703
- dateInput: 'YYYY-MM-DD',
4704
- monthYearLabel: 'MMM YYYY',
4705
- dateA11yLabel: 'YYYY-MM-DD',
4706
- monthYearA11yLabel: 'MMMM YYYY',
4707
- },
4708
- };
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
+
4709
4829
  class PropertyOfferDetailComponent extends BaseComponent {
4710
- propertyService = inject(PropertyService);
4711
4830
  propertyOfferService = inject(PropertyOfferService);
4712
- propertyOfferCounterService = inject(PropertyOfferCounterService);
4713
4831
  f = inject(PropertyOfferNegotiationFacade);
4714
- authUserService = inject(AuthUserService);
4715
- paymentService = inject(PaymentService);
4716
4832
  ctx = inject(OfferDetailContext);
4717
- clipboard = inject(Clipboard);
4718
- isRental = computed(() => this.offer.type.toString() === 'RENTAL', ...(ngDevMode ? [{ debugName: "isRental" }] : []));
4719
- isSale = computed(() => this.offer.type.toString() === 'SALE', ...(ngDevMode ? [{ debugName: "isSale" }] : []));
4720
- expandedTenantIndexes = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedTenantIndexes" }] : []));
4721
- view;
4722
- offer;
4723
- info = false;
4724
- loadingTimeline = false;
4725
- timelineData;
4726
- status = PropertyOfferStatus;
4727
- timelineStatus = PropertyOfferTimelineStatus;
4728
- env = inject(APP_CONFIG);
4729
- location = inject(Location);
4730
- statusLabel = computed(() => offerStatusLabel(this.f.offer()?.status), ...(ngDevMode ? [{ debugName: "statusLabel" }] : []));
4731
- property;
4732
- agent;
4733
- checkouting = false;
4734
- holdingDeposit;
4735
- securityDeposit;
4736
- type = computed(() => {
4737
- return this.offer.type.toString();
4738
- }, ...(ngDevMode ? [{ debugName: "type" }] : []));
4739
- async ngOnInit() {
4740
- this.route.params.subscribe((params) => {
4741
- const id = params['id'];
4742
- this.ctx.init(id);
4743
- if (id)
4744
- this.f.load(id, 'tenant');
4745
- this.get(id);
4746
- // this.getViewByTenant(id);
4747
- });
4748
- this.route.queryParams.subscribe(async (params) => {
4749
- const sessionId = params['session_id'];
4750
- if (sessionId) {
4751
- this.propertyOfferService.checkOfferPaymentStatus(this.id, sessionId).subscribe({
4752
- next: (res) => {
4753
- if (res.status === 'paid') {
4754
- this.offer.status = PropertyOfferStatus.HOLDING_DEPOSIT_PAID;
4755
- // Show success UI
4756
- }
4757
- },
4758
- });
4759
- }
4760
- });
4761
- }
4762
- ngOnDestroy() {
4763
- this.checkouting = false;
4764
- }
4765
- get(id) {
4766
- this.propertyOfferService.getOffer(id).subscribe({
4767
- next: (res) => {
4768
- this.offer = res.data;
4769
- this.getProperty(this.offer.propertyId);
4770
- this.calculateDeposit();
4771
- // this.findAgentInfo();
4772
- },
4773
- });
4774
- }
4775
- getViewByTenant(id) {
4776
- this.propertyOfferCounterService.getNegotiationViewByTenant(id).subscribe({
4777
- next: (res) => {
4778
- this.view = res.data;
4779
- },
4780
- });
4781
- }
4782
- timeline() {
4783
- this.info = true;
4784
- this.loadingTimeline = true;
4785
- this.propertyOfferService.offerTimeline(this.id).subscribe({
4786
- next: (res) => {
4787
- this.timelineData = res.data;
4788
- this.loadingTimeline = false;
4789
- },
4790
- error: (error) => {
4791
- this.loadingTimeline = false;
4792
- },
4793
- });
4794
- }
4795
- getProperty(propertyId) {
4796
- this.propertyService.get(propertyId).subscribe({
4797
- next: (res) => {
4798
- this.property = res.data;
4799
- this.getAgentPublicInfo(this.property.agentId);
4800
- },
4801
- });
4802
- }
4803
- getAgentPublicInfo(agentId) {
4804
- this.authUserService.getPublicUserInfo(agentId).subscribe({
4805
- next: (res) => {
4806
- this.agent = res;
4807
- },
4808
- });
4809
- }
4810
- cancel() {
4811
- this.propertyOfferService.cancelOffer(this.id).subscribe({
4812
- next: (res) => {
4813
- this.offer.status = 'CANCELLED';
4814
- this.snackBarService.open('Cancelled');
4815
- },
4816
- error: (error) => {
4817
- this.snackBarService.open(error.message);
4818
- },
4819
- });
4820
- }
4821
- calculateDeposit() {
4822
- const price = this.offer.item.amount;
4823
- this.holdingDeposit = ((price * 12) / 52).toFixed(2);
4824
- this.securityDeposit = (((price * 12) / 52) * 5).toFixed(2);
4825
- }
4826
- holdingDepositCheckout() {
4827
- this.checkouting = true;
4828
- const data = {
4829
- businessType: 'OFFER',
4830
- businessId: this.id,
4831
- provider: 'STRIPE',
4832
- method: 'STRIPE_CHECKOUT',
4833
- purpose: 'HOLDING_DEPOSIT',
4834
- };
4835
- this.paymentService
4836
- .createPaymentIntent(data)
4837
- .pipe(switchMap((res) => {
4838
- const intent = {
4839
- intentId: res.data.id,
4840
- provider: 'STRIPE',
4841
- method: 'STRIPE_CHECKOUT',
4842
- successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
4843
- cancelUrl: window.location.href,
4844
- };
4845
- return this.paymentService.createPayment(intent);
4846
- }))
4847
- .subscribe({
4848
- next: (res) => {
4849
- this.checkouting = false;
4850
- const { checkoutUrl } = res.data;
4851
- window.open(checkoutUrl, '_blank');
4852
- },
4853
- error: (error) => {
4854
- this.checkouting = false;
4855
- },
4856
- });
4857
- }
4858
- securityDepositCheckout() {
4859
- this.checkouting = true;
4860
- const data = {
4861
- successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
4862
- cancelUrl: window.location.href,
4863
- };
4864
- this.propertyOfferService.createSecurityDepositCheckout(this.id, data).subscribe({
4865
- next: (res) => {
4866
- window.location.href = res.data.checkoutUrl;
4867
- },
4868
- error: (error) => {
4869
- this.checkouting = false;
4833
+ mode = input('tenant', ...(ngDevMode ? [{ debugName: "mode" }] : []));
4834
+ market = input('tenant', ...(ngDevMode ? [{ debugName: "market" }] : []));
4835
+ eyebrow = input(null, ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
4836
+ title = input(null, ...(ngDevMode ? [{ debugName: "title" }] : []));
4837
+ description = input(null, ...(ngDevMode ? [{ debugName: "description" }] : []));
4838
+ backLabel = input(null, ...(ngDevMode ? [{ debugName: "backLabel" }] : []));
4839
+ currentOfferId = this.id ?? null;
4840
+ statusLabel = computed(() => offerStatusLabel(this.ctx.offer()?.status), ...(ngDevMode ? [{ debugName: "statusLabel" }] : []));
4841
+ resolvedEyebrow = computed(() => this.eyebrow() ?? (this.mode() === 'agent' ? 'Market workflow' : 'My applications'), ...(ngDevMode ? [{ debugName: "resolvedEyebrow" }] : []));
4842
+ resolvedTitle = computed(() => this.title() ?? (this.mode() === 'agent' ? 'Offer detail' : 'Offer detail'), ...(ngDevMode ? [{ debugName: "resolvedTitle" }] : []));
4843
+ resolvedDescription = computed(() => this.description() ??
4844
+ (this.mode() === 'agent'
4845
+ ? 'Track buyer-side negotiations, compare counter-offers, and keep the deal moving from one shared workspace.'
4846
+ : 'Review every negotiation update, compare offer versions, and keep the next step close at hand.'), ...(ngDevMode ? [{ debugName: "resolvedDescription" }] : []));
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',
4870
4863
  },
4871
- });
4872
- }
4873
- copyText(redactContact) {
4874
- this.propertyOfferService.getCopyText(this.id).subscribe({
4875
- next: (res) => {
4876
- this.clipboard.copy(res.data.text);
4877
- 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',
4878
4884
  },
4879
- });
4880
- }
4881
- reject() {
4882
- const ref = this.dialog.open(RejectDialogComponent, {
4883
- width: '420px',
4884
- });
4885
- ref.afterClosed().subscribe((notes) => {
4886
- if (notes) {
4887
- this.f.reject(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;
4888
4893
  }
4894
+ this.ctx.init(id);
4895
+ void this.f.load(id, this.market());
4889
4896
  });
4890
- }
4891
- openReferenceFailedDialog() {
4892
- const ref = this.dialog.open(RejectDialogComponent, {
4893
- width: '420px',
4894
- });
4895
- ref.afterClosed().subscribe((notes) => {
4896
- if (notes) {
4897
- this.f.markReferencesFailed(notes);
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;
4898
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.market());
4909
+ }
4910
+ },
4911
+ });
4899
4912
  });
4900
4913
  }
4901
- //COUNTER
4902
- counter() {
4903
- const options = {
4904
- width: '500px',
4905
- title: 'Counter offer',
4906
- height: '90%',
4907
- cancelText: 'Cancel',
4908
- confirmText: 'Confirm',
4909
- component: OfferCounterDialog,
4910
- data: {
4911
- term: this.offer.rentalTerms,
4912
- },
4913
- };
4914
- this.dialogService.open(options);
4915
- this.dialogService.confirmed().subscribe((res) => {
4916
- if (res) {
4917
- console.log(res);
4918
- this.propertyOfferCounterService.counterByTenant(this.id, res).subscribe({
4919
- next: (res) => {
4920
- this.snackBarService.open('Counter sent');
4921
- },
4922
- error: (error) => {
4923
- this.snackBarService.open(error.message);
4924
- },
4925
- });
4926
- }
4927
- });
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());
4928
4933
  }
4929
- withdraw() { }
4930
4934
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4931
- 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 }, market: { classPropertyName: "market", publicName: "market", 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 <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=\"../\">Back to offers</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" }] });
4932
4936
  }
4933
4937
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, decorators: [{
4934
4938
  type: Component,
4935
4939
  args: [{ selector: 'rolatech-property-offer-detail', imports: [
4936
4940
  CommonModule,
4941
+ RouterLink,
4937
4942
  MatButtonModule,
4938
- MatIconModule,
4939
- MatProgressSpinnerModule,
4943
+ PageCollectionShellComponent,
4944
+ PropertyWorkspaceShell,
4940
4945
  OfferHeaderCard,
4941
4946
  OfferItemCard,
4942
4947
  OfferRentalTermsCard,
@@ -4944,48 +4949,195 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4944
4949
  OfferTenantsAccordion,
4945
4950
  OfferSideSummaryCard,
4946
4951
  OfferHistoryAccordion,
4947
- MatFormFieldModule,
4948
- MatInputModule,
4949
- MatSelectModule,
4950
- MatDatepickerModule,
4951
- MatNativeDateModule,
4952
- MatSlideToggleModule,
4953
- MatChipsModule,
4954
- FormsModule,
4955
4952
  OfferCounterPreviousCard,
4956
4953
  OfferCounterLatestCard,
4957
4954
  OfferNegotiationSection,
4958
- ], 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" }]
4959
- }] });
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 <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=\"../\">Back to offers</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 }] }], market: [{ type: i0.Input, args: [{ isSignal: true, alias: "market", 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 }] }] } });
4960
4957
 
4961
- class PropertyViewingDetailComponent extends BaseComponent {
4958
+ class ViewingDetailContext {
4962
4959
  propertyService = inject(PropertyService);
4963
- acceptingSlotId = signal(null, ...(ngDevMode ? [{ debugName: "acceptingSlotId" }] : []));
4964
- viewing;
4965
- applicantType = PropertyApplicantType;
4966
- async ngOnInit() {
4967
- this.route.params.subscribe((params) => {
4968
- const id = params['id'];
4969
- 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);
4970
4971
  });
4971
4972
  }
4972
- get(id) {
4973
- this.propertyService.getViewing(id).subscribe({
4974
- next: (res) => {
4975
- this.viewing = res.data;
4976
- this.titleService.setTitle(this.viewing.item?.title || 'Viewing detail');
4977
- },
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');
4978
5131
  });
4979
5132
  }
4980
- cancel() {
4981
- this.propertyService.cancelViewing(this.id).subscribe({
4982
- next: (res) => {
4983
- this.viewing.status = 'CANCELLED';
4984
- this.snackBarService.open('Cancelled');
4985
- },
4986
- error: (error) => {
4987
- this.snackBarService.open(error.message);
4988
- },
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);
4989
5141
  });
4990
5142
  }
4991
5143
  statusLabel(status) {
@@ -5004,10 +5156,10 @@ class PropertyViewingDetailComponent extends BaseComponent {
5004
5156
  }
5005
5157
  }
5006
5158
  statusHeadline() {
5007
- if (this.requiresSlotAcceptance()) {
5159
+ if (this.facade.requiresSlotAcceptance()) {
5008
5160
  return 'The listing team returned new slots for you to choose from.';
5009
5161
  }
5010
- switch (propertyViewingStatusCode(this.viewing?.status)) {
5162
+ switch (propertyViewingStatusCode(this.viewing()?.status)) {
5011
5163
  case 'COUNTERED':
5012
5164
  return 'The listing team returned new slots for you to choose from.';
5013
5165
  case 'APPROVED':
@@ -5023,16 +5175,17 @@ class PropertyViewingDetailComponent extends BaseComponent {
5023
5175
  }
5024
5176
  }
5025
5177
  statusDescription() {
5026
- if (!this.viewing) {
5178
+ const viewing = this.viewing();
5179
+ if (!viewing) {
5027
5180
  return '';
5028
5181
  }
5029
- if (this.requiresSlotAcceptance()) {
5182
+ if (this.facade.requiresSlotAcceptance()) {
5030
5183
  return 'Review the three returned options below and accept the slot that works best for you.';
5031
5184
  }
5032
5185
  if (this.confirmedSlotLabel()) {
5033
5186
  return `Confirmed for ${this.confirmedSlotLabel()}.`;
5034
5187
  }
5035
- switch (propertyViewingStatusCode(this.viewing.status)) {
5188
+ switch (propertyViewingStatusCode(viewing.status)) {
5036
5189
  case 'REJECTED':
5037
5190
  return 'Review the proposed slots and submit a new request if you still want to visit the property.';
5038
5191
  case 'CANCELLED':
@@ -5044,34 +5197,38 @@ class PropertyViewingDetailComponent extends BaseComponent {
5044
5197
  }
5045
5198
  }
5046
5199
  confirmedSlotLabel() {
5047
- if (!this.viewing?.viewingDate) {
5200
+ const viewing = this.viewing();
5201
+ if (!viewing?.viewingDate) {
5048
5202
  return null;
5049
5203
  }
5050
- const date = new Date(this.viewing.viewingDate);
5204
+ const date = new Date(viewing.viewingDate);
5051
5205
  const hasValidDate = !Number.isNaN(date.getTime());
5052
5206
  const formattedDate = hasValidDate
5053
5207
  ? new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium' }).format(date)
5054
- : this.viewing.viewingDate;
5055
- return this.viewing.viewingTime ? `${formattedDate} at ${this.viewing.viewingTime}` : formattedDate;
5208
+ : viewing.viewingDate;
5209
+ return viewing.viewingTime ? `${formattedDate} at ${viewing.viewingTime}` : formattedDate;
5056
5210
  }
5057
5211
  isConfirmedSlot(slot) {
5058
- 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;
5059
5214
  }
5060
5215
  primaryMediaUrl() {
5061
- return this.viewing?.item?.media?.[0]?.url || '';
5216
+ return this.viewing()?.item?.media?.[0]?.url || '';
5062
5217
  }
5063
5218
  requesterName() {
5064
- const fullName = `${this.viewing?.firstName ?? ''} ${this.viewing?.lastName ?? ''}`.trim();
5219
+ const viewing = this.viewing();
5220
+ const fullName = `${viewing?.firstName ?? ''} ${viewing?.lastName ?? ''}`.trim();
5065
5221
  return fullName || 'Viewer';
5066
5222
  }
5067
5223
  contactSummary() {
5068
- if (!this.viewing) {
5224
+ const viewing = this.viewing();
5225
+ if (!viewing) {
5069
5226
  return 'Contact details available below';
5070
5227
  }
5071
- if (this.viewing.email && this.viewing.phone) {
5072
- return `${this.viewing.email} · ${this.viewing.phone}`;
5228
+ if (viewing.email && viewing.phone) {
5229
+ return `${viewing.email} · ${viewing.phone}`;
5073
5230
  }
5074
- return this.viewing.email || this.viewing.phone || 'Contact details available below';
5231
+ return viewing.email || viewing.phone || 'Contact details available below';
5075
5232
  }
5076
5233
  viewerCategoryLabel(value) {
5077
5234
  if (!value) {
@@ -5093,50 +5250,6 @@ class PropertyViewingDetailComponent extends BaseComponent {
5093
5250
  requesterTypeLabel(value) {
5094
5251
  return value ? this.humanize(value) : 'Direct request';
5095
5252
  }
5096
- requiresSlotAcceptance() {
5097
- return (!this.viewing?.viewingDate &&
5098
- (this.viewing?.slotDecisionRequired === true ||
5099
- this.slotProposalSource() === 'ADMIN_COUNTER' ||
5100
- propertyViewingStatusCode(this.viewing?.status) === 'COUNTERED'));
5101
- }
5102
- canAcceptCounterSlot(slot) {
5103
- return this.requiresSlotAcceptance() && !!slot?.id && this.acceptingSlotId() !== slot.id;
5104
- }
5105
- acceptCounterSlot(slot) {
5106
- if (!slot?.id) {
5107
- this.snackBarService.open('This slot cannot be accepted because the slot id is missing.');
5108
- return;
5109
- }
5110
- this.acceptingSlotId.set(slot.id);
5111
- this.propertyService.confirmViewing(this.id, slot.id).subscribe({
5112
- next: (res) => {
5113
- const nextViewing = res?.data;
5114
- this.viewing = {
5115
- ...this.viewing,
5116
- ...nextViewing,
5117
- status: nextViewing?.status ?? 'APPROVED',
5118
- viewingDate: nextViewing?.viewingDate ?? slot.date ?? this.viewing.viewingDate,
5119
- viewingTime: nextViewing?.viewingTime ?? slot.time ?? this.viewing.viewingTime,
5120
- proposedSlots: nextViewing?.proposedSlots ?? this.viewing.proposedSlots,
5121
- item: nextViewing?.item ?? this.viewing.item,
5122
- slotProposalSource: nextViewing?.slotProposalSource ?? this.viewing.slotProposalSource,
5123
- slotDecisionRequired: nextViewing?.slotDecisionRequired ?? false,
5124
- };
5125
- this.snackBarService.open('Viewing confirmed.');
5126
- },
5127
- error: (error) => {
5128
- this.acceptingSlotId.set(null);
5129
- this.snackBarService.open(error.message);
5130
- },
5131
- complete: () => {
5132
- this.acceptingSlotId.set(null);
5133
- },
5134
- });
5135
- }
5136
- canCancel() {
5137
- const code = propertyViewingStatusCode(this.viewing?.status);
5138
- return code !== 'CANCELLED' && code !== 'COMPLETED';
5139
- }
5140
5253
  humanize(value) {
5141
5254
  return value
5142
5255
  .trim()
@@ -5144,16 +5257,8 @@ class PropertyViewingDetailComponent extends BaseComponent {
5144
5257
  .toLowerCase()
5145
5258
  .replace(/\b\w/g, (char) => char.toUpperCase());
5146
5259
  }
5147
- slotProposalSource() {
5148
- const explicitSource = this.viewing?.slotProposalSource;
5149
- if (explicitSource) {
5150
- return explicitSource.toUpperCase();
5151
- }
5152
- const slotSource = this.viewing?.proposedSlots?.[0]?.source;
5153
- return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
5154
- }
5155
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
5156
- 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\">\n @if (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 (requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"acceptingSlotId() === slot.id\"\n (click)=\"acceptCounterSlot(slot)\"\n >\n @if (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 (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]] });
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]] });
5157
5262
  }
5158
5263
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, decorators: [{
5159
5264
  type: Component,
@@ -5167,8 +5272,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
5167
5272
  ToolbarComponent,
5168
5273
  RouterLink,
5169
5274
  PricePipe,
5170
- ], 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\">\n @if (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 (requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"acceptingSlotId() === slot.id\"\n (click)=\"acceptCounterSlot(slot)\"\n >\n @if (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 (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"] }]
5171
- }] });
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: () => [] });
5172
5277
 
5173
5278
  class PropertyWishlistComponent extends BaseComponent {
5174
5279
  propertyService = inject(PropertyService);
@@ -7079,7 +7184,7 @@ class PropertyOfferCreate {
7079
7184
  }
7080
7185
  }
7081
7186
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, deps: [], target: i0.ɵɵFactoryTarget.Component });
7082
- 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"] }] });
7083
7188
  }
7084
7189
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, decorators: [{
7085
7190
  type: Component,
@@ -7090,7 +7195,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
7090
7195
  PropertyOfferSaleForm,
7091
7196
  PropertyIntentShell,
7092
7197
  PropertyMarketBreadcrumbComponent,
7093
- ], 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"] }]
7094
7199
  }], ctorParameters: () => [] });
7095
7200
 
7096
7201
  const propertyRoutes = [
@@ -7100,7 +7205,7 @@ const propertyRoutes = [
7100
7205
  children: [
7101
7206
  {
7102
7207
  path: '',
7103
- loadComponent: () => import('./rolatech-angular-property-property-index.component-CbvPpms7.mjs').then((x) => x.PropertyIndexComponent),
7208
+ loadComponent: () => import('./rolatech-angular-property-property-index.component-2BXmI0uJ.mjs').then((x) => x.PropertyIndexComponent),
7104
7209
  },
7105
7210
  ],
7106
7211
  },
@@ -9262,7 +9367,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9262
9367
  const propertyManageViewingsRoutes = [
9263
9368
  {
9264
9369
  path: '',
9265
- loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-Bvxmxhbu.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
9370
+ loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-Cws6ElRk.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
9266
9371
  },
9267
9372
  {
9268
9373
  path: ':id',
@@ -9311,19 +9416,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9311
9416
  args: ['class.rolatech-property-market-item']
9312
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 }] }] } });
9313
9418
 
9314
- class PropertyWorkspaceShell {
9315
- eyebrow = input('Workspace', ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
9316
- title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
9317
- description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
9318
- stats = input([], ...(ngDevMode ? [{ debugName: "stats" }] : []));
9319
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
9320
- 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 }] });
9321
- }
9322
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, decorators: [{
9323
- type: Component,
9324
- 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"] }]
9325
- }], 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 }] }] } });
9326
-
9327
9419
  class PropertyMarketIndex extends BaseComponent {
9328
9420
  propertySearchService = inject(PropertySearchService);
9329
9421
  loading = false;
@@ -9798,10 +9890,22 @@ class PropertyMarketViewingIndex extends BaseComponent {
9798
9890
  stats = computed(() => {
9799
9891
  const viewings = this.viewings();
9800
9892
  return [
9801
- { label: 'Buyer requests', value: this.length || viewings.length, hint: 'Visits requested while you are acting as a buyer' },
9802
- { label: 'Pending', value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'PENDING').length, hint: 'Waiting for listing-side review' },
9803
- { label: 'Approved', value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'APPROVED').length, hint: 'Confirmed appointments in your buyer workspace' },
9804
- { 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
+ },
9805
9909
  ];
9806
9910
  }, ...(ngDevMode ? [{ debugName: "stats" }] : []));
9807
9911
  ngOnInit() {
@@ -9850,7 +9954,7 @@ class PropertyMarketViewingIndex extends BaseComponent {
9850
9954
  });
9851
9955
  }
9852
9956
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
9853
- 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"] }] });
9854
9958
  }
9855
9959
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, decorators: [{
9856
9960
  type: Component,
@@ -9861,7 +9965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9861
9965
  PropertyViewingItemComponent,
9862
9966
  MatPaginatorModule,
9863
9967
  PropertyWorkspaceShell,
9864
- ], 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"] }]
9865
9969
  }] });
9866
9970
 
9867
9971
  class PropertyMarketViewingDetail {
@@ -9880,10 +9984,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9880
9984
 
9881
9985
  class PropertyMarketOfferIndex extends BaseComponent {
9882
9986
  propertyOfferService = inject(PropertyOfferService);
9883
- breadcrumbs = [
9884
- { label: 'Properties', link: '/properties' },
9885
- { label: 'Offers' },
9886
- ];
9987
+ breadcrumbs = [{ label: 'Properties', link: '/properties' }, { label: 'Offers' }];
9887
9988
  select = 0;
9888
9989
  links = [
9889
9990
  { name: 'All', status: '' },
@@ -9902,10 +10003,22 @@ class PropertyMarketOfferIndex extends BaseComponent {
9902
10003
  stats = computed(() => {
9903
10004
  const offers = this.offers();
9904
10005
  return [
9905
- { label: 'Buyer offers', value: this.length || offers.length, hint: 'Offers you submitted while acting as a buyer agent' },
9906
- { label: 'Submitted', value: offers.filter((item) => item.status === PropertyOfferStatus.SUBMITTED).length, hint: 'Waiting for listing-side review or response' },
9907
- { label: 'Accepted', value: offers.filter((item) => item.status === PropertyOfferStatus.ACCEPTED).length, hint: 'Offers moved forward in the negotiation flow' },
9908
- { 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
+ },
9909
10022
  ];
9910
10023
  }, ...(ngDevMode ? [{ debugName: "stats" }] : []));
9911
10024
  ngOnInit() {
@@ -9954,7 +10067,7 @@ class PropertyMarketOfferIndex extends BaseComponent {
9954
10067
  });
9955
10068
  }
9956
10069
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
9957
- 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 });
9958
10071
  }
9959
10072
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, decorators: [{
9960
10073
  type: Component,
@@ -9966,7 +10079,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9966
10079
  MatPaginatorModule,
9967
10080
  OfferItemSkeleton,
9968
10081
  PropertyWorkspaceShell,
9969
- ], 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"] }]
9970
10083
  }] });
9971
10084
 
9972
10085
  class PropertyMarketOfferDetail {
@@ -9976,11 +10089,11 @@ class PropertyMarketOfferDetail {
9976
10089
  { label: 'Offer detail' },
9977
10090
  ];
9978
10091
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
9979
- 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 [market]=\"'tenant'\"\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", "market", "eyebrow", "title", "description", "backLabel"] }] });
9980
10093
  }
9981
10094
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, decorators: [{
9982
10095
  type: Component,
9983
- 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 [market]=\"'tenant'\"\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"] }]
9984
10097
  }] });
9985
10098
 
9986
10099
  class PropertyMarketViewingRequest extends BaseComponent {
@@ -10073,7 +10186,7 @@ class PropertyMarketViewingRequest extends BaseComponent {
10073
10186
  this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
10074
10187
  }
10075
10188
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
10076
- 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"] }, { 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" }] });
10077
10190
  }
10078
10191
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, decorators: [{
10079
10192
  type: Component,
@@ -10092,7 +10205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
10092
10205
  PropertyPricingComponent,
10093
10206
  PropertyMarketBreadcrumbComponent,
10094
10207
  MatSelectModule,
10095
- ], 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"] }]
10096
10209
  }], ctorParameters: () => [] });
10097
10210
 
10098
10211
  class PropertyMarketOfferRequest extends BaseComponent {
@@ -10190,7 +10303,7 @@ class PropertyMarketOfferRequest extends BaseComponent {
10190
10303
  }
10191
10304
  }
10192
10305
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
10193
- 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" }] });
10194
10307
  }
10195
10308
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, decorators: [{
10196
10309
  type: Component,
@@ -10203,7 +10316,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
10203
10316
  PropertyMarketBreadcrumbComponent,
10204
10317
  PropertyOfferRentalForm,
10205
10318
  PropertyOfferSaleForm,
10206
- ], 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"] }]
10207
10320
  }], ctorParameters: () => [] });
10208
10321
 
10209
10322
  class PropertyMarketSavedIndex {
@@ -11542,4 +11655,4 @@ function provideAngularPropertyFeature() {
11542
11655
  */
11543
11656
 
11544
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 };
11545
- //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-BipC43h_.mjs.map
11658
+ //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-CKEFhpfX.mjs.map