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

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.
@@ -127,6 +127,7 @@ var PropertyScope;
127
127
  var PropertyViewingStatus;
128
128
  (function (PropertyViewingStatus) {
129
129
  PropertyViewingStatus["PENDING"] = "Pending";
130
+ PropertyViewingStatus["COUNTERED"] = "Countered";
130
131
  PropertyViewingStatus["APPROVED"] = "Approved";
131
132
  PropertyViewingStatus["REJECTED"] = "Rejected";
132
133
  PropertyViewingStatus["CANCELLED"] = "Cancelled";
@@ -4101,7 +4102,6 @@ class PropertyViewingTimeComponent {
4101
4102
  minDate = new Date();
4102
4103
  maxDate = new Date();
4103
4104
  output = output();
4104
- select = output();
4105
4105
  proposedTime = input({
4106
4106
  date: '',
4107
4107
  time: '',
@@ -4111,18 +4111,56 @@ class PropertyViewingTimeComponent {
4111
4111
  this.minDate.setDate(this.minDate.getDate() + 1);
4112
4112
  this.maxDate.setDate(this.maxDate.getDate() + 7);
4113
4113
  }
4114
- ngDoCheck() {
4115
- this.output.emit(this.proposedTime());
4114
+ onDateInput(event) {
4115
+ this.updateSlot({
4116
+ date: this.formatDateValue(event.value),
4117
+ });
4118
+ }
4119
+ onTimeChange(time) {
4120
+ this.updateSlot({
4121
+ time: time ?? '',
4122
+ });
4123
+ }
4124
+ updateSlot(changes) {
4125
+ const current = this.proposedTime() ?? {
4126
+ date: '',
4127
+ time: '',
4128
+ };
4129
+ const next = {
4130
+ ...current,
4131
+ ...changes,
4132
+ };
4133
+ if (current.date === next.date && current.time === next.time) {
4134
+ return;
4135
+ }
4136
+ current.date = next.date;
4137
+ current.time = next.time;
4138
+ this.output.emit(next);
4139
+ }
4140
+ formatDateValue(value) {
4141
+ if (!value) {
4142
+ return '';
4143
+ }
4144
+ if (typeof value === 'object' && value !== null && 'format' in value && typeof value.format === 'function') {
4145
+ return value.format('YYYY-MM-DD');
4146
+ }
4147
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
4148
+ const year = value.getFullYear();
4149
+ const month = String(value.getMonth() + 1).padStart(2, '0');
4150
+ const day = String(value.getDate()).padStart(2, '0');
4151
+ return `${year}-${month}-${day}`;
4152
+ }
4153
+ return String(value);
4116
4154
  }
4117
4155
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4118
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingTimeComponent, isStandalone: true, selector: "rolatech-property-viewing-time", inputs: { proposedTime: { classPropertyName: "proposedTime", publicName: "proposedTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { output: "output", select: "select" }, providers: [
4156
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingTimeComponent, isStandalone: true, selector: "rolatech-property-viewing-time", inputs: { proposedTime: { classPropertyName: "proposedTime", publicName: "proposedTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { output: "output" }, providers: [
4119
4157
  {
4120
4158
  provide: DateAdapter,
4121
4159
  useClass: MomentDateAdapter,
4122
4160
  deps: [MAT_DATE_LOCALE],
4123
4161
  },
4124
4162
  { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
4125
- ], ngImport: i0, template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [(ngModel)]=\"proposedTime()!.date\"\n (dateInput)=\"proposedTime()!.date = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [(ngModel)]=\"proposedTime()!.time\" required readonly>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }] });
4163
+ ], ngImport: i0, template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [ngModel]=\"proposedTime().date || null\"\n (dateInput)=\"onDateInput($event)\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [ngModel]=\"proposedTime().time || null\" (ngModelChange)=\"onTimeChange($event)\" required>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }] });
4126
4164
  }
4127
4165
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, decorators: [{
4128
4166
  type: Component,
@@ -4142,8 +4180,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4142
4180
  deps: [MAT_DATE_LOCALE],
4143
4181
  },
4144
4182
  { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
4145
- ], template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [(ngModel)]=\"proposedTime()!.date\"\n (dateInput)=\"proposedTime()!.date = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [(ngModel)]=\"proposedTime()!.time\" required readonly>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
4146
- }], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], select: [{ type: i0.Output, args: ["select"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
4183
+ ], 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
+ }], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
4147
4185
 
4148
4186
  class PropertyIntentShell {
4149
4187
  property = input(null, ...(ngDevMode ? [{ debugName: "property" }] : []));
@@ -4266,7 +4304,7 @@ class PropertyViewingRequestComponent extends BaseComponent {
4266
4304
  this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
4267
4305
  }
4268
4306
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4269
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", usesInheritance: true, ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Buyer workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n\n <div class=\"property-viewing-request__slots\">\n <div class=\"mb-3\">\n <div class=\"text-lg font-bold\" i18n>Viewing date</div>\n <div class=\"opacity-70\" i18n>\n Please choose 3 different times on at least 2 different days that would work for you.\n </div>\n </div>\n\n @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n </div>\n</rolatech-property-intent-shell>\n", styles: ["mat-form-field{width:100%}.property-viewing-request__header,.property-viewing-request__breadcrumb{margin-bottom:1rem}.property-viewing-request__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-request__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-viewing-request__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-request__form{display:flex;flex-direction:column;gap:.75rem}.property-viewing-request__grid{display:grid;gap:.75rem}.property-viewing-request__slots{margin-top:.25rem}@media(min-width:768px){.property-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output", "select"] }, { kind: "component", type: PropertyIntentShell, selector: "rolatech-property-intent-shell", inputs: ["property"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
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" }] });
4270
4308
  }
4271
4309
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, decorators: [{
4272
4310
  type: Component,
@@ -4922,6 +4960,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
4922
4960
 
4923
4961
  class PropertyViewingDetailComponent extends BaseComponent {
4924
4962
  propertyService = inject(PropertyService);
4963
+ acceptingSlotId = signal(null, ...(ngDevMode ? [{ debugName: "acceptingSlotId" }] : []));
4925
4964
  viewing;
4926
4965
  applicantType = PropertyApplicantType;
4927
4966
  async ngOnInit() {
@@ -4965,7 +5004,12 @@ class PropertyViewingDetailComponent extends BaseComponent {
4965
5004
  }
4966
5005
  }
4967
5006
  statusHeadline() {
5007
+ if (this.requiresSlotAcceptance()) {
5008
+ return 'The listing team returned new slots for you to choose from.';
5009
+ }
4968
5010
  switch (propertyViewingStatusCode(this.viewing?.status)) {
5011
+ case 'COUNTERED':
5012
+ return 'The listing team returned new slots for you to choose from.';
4969
5013
  case 'APPROVED':
4970
5014
  return 'Your viewing is confirmed.';
4971
5015
  case 'COMPLETED':
@@ -4982,6 +5026,9 @@ class PropertyViewingDetailComponent extends BaseComponent {
4982
5026
  if (!this.viewing) {
4983
5027
  return '';
4984
5028
  }
5029
+ if (this.requiresSlotAcceptance()) {
5030
+ return 'Review the three returned options below and accept the slot that works best for you.';
5031
+ }
4985
5032
  if (this.confirmedSlotLabel()) {
4986
5033
  return `Confirmed for ${this.confirmedSlotLabel()}.`;
4987
5034
  }
@@ -5046,6 +5093,46 @@ class PropertyViewingDetailComponent extends BaseComponent {
5046
5093
  requesterTypeLabel(value) {
5047
5094
  return value ? this.humanize(value) : 'Direct request';
5048
5095
  }
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
+ }
5049
5136
  canCancel() {
5050
5137
  const code = propertyViewingStatusCode(this.viewing?.status);
5051
5138
  return code !== 'CANCELLED' && code !== 'COMPLETED';
@@ -5057,8 +5144,16 @@ class PropertyViewingDetailComponent extends BaseComponent {
5057
5144
  .toLowerCase()
5058
5145
  .replace(/\b\w/g, (char) => char.toUpperCase());
5059
5146
  }
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
+ }
5060
5155
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
5061
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Availability shared with the listing team</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\" i18n>\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
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]] });
5062
5157
  }
5063
5158
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, decorators: [{
5064
5159
  type: Component,
@@ -5072,7 +5167,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
5072
5167
  ToolbarComponent,
5073
5168
  RouterLink,
5074
5169
  PricePipe,
5075
- ], template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Availability shared with the listing team</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\" i18n>\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"] }]
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"] }]
5076
5171
  }] });
5077
5172
 
5078
5173
  class PropertyWishlistComponent extends BaseComponent {
@@ -7005,7 +7100,7 @@ const propertyRoutes = [
7005
7100
  children: [
7006
7101
  {
7007
7102
  path: '',
7008
- loadComponent: () => import('./rolatech-angular-property-property-index.component-De4homi3.mjs').then((x) => x.PropertyIndexComponent),
7103
+ loadComponent: () => import('./rolatech-angular-property-property-index.component-CbvPpms7.mjs').then((x) => x.PropertyIndexComponent),
7009
7104
  },
7010
7105
  ],
7011
7106
  },
@@ -8860,6 +8955,9 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8860
8955
  propertyService = inject(PropertyService);
8861
8956
  authUserService = inject(AuthUserService);
8862
8957
  applicantType = PropertyApplicantType;
8958
+ counterMode = signal(false, ...(ngDevMode ? [{ debugName: "counterMode" }] : []));
8959
+ countering = signal(false, ...(ngDevMode ? [{ debugName: "countering" }] : []));
8960
+ counterSlots = signal(this.createCounterSlots(), ...(ngDevMode ? [{ debugName: "counterSlots" }] : []));
8863
8961
  viewing;
8864
8962
  agent;
8865
8963
  ngOnInit() {
@@ -8870,6 +8968,7 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8870
8968
  next: (res) => {
8871
8969
  this.viewing = res.data;
8872
8970
  this.titleService.setTitle(this.viewing.item?.title || 'Manage viewing request');
8971
+ this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
8873
8972
  if (this.viewing.agentId) {
8874
8973
  this.getAgentPublicInfo(this.viewing.agentId);
8875
8974
  }
@@ -8914,6 +9013,8 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8914
9013
  proposedSlots: res?.data?.proposedSlots ?? this.viewing.proposedSlots,
8915
9014
  item: res?.data?.item ?? this.viewing.item,
8916
9015
  };
9016
+ this.counterMode.set(false);
9017
+ this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
8917
9018
  this.snackBarService.open('Viewing confirmed');
8918
9019
  },
8919
9020
  error: (error) => {
@@ -8939,7 +9040,12 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8939
9040
  }
8940
9041
  }
8941
9042
  statusHeadline() {
9043
+ if (this.isAwaitingAcceptance()) {
9044
+ return 'Counter slots sent. Waiting for the requester to pick one.';
9045
+ }
8942
9046
  switch (propertyViewingStatusCode(this.viewing?.status)) {
9047
+ case 'COUNTERED':
9048
+ return 'Counter slots sent. Waiting for the requester to pick one.';
8943
9049
  case 'APPROVED':
8944
9050
  return 'Viewing confirmed with the applicant.';
8945
9051
  case 'COMPLETED':
@@ -8956,6 +9062,9 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8956
9062
  if (!this.viewing) {
8957
9063
  return '';
8958
9064
  }
9065
+ if (this.isAwaitingAcceptance()) {
9066
+ return 'The original requested times did not fit the diary, so three new options have been returned for the requester to accept.';
9067
+ }
8959
9068
  if (this.confirmedSlotLabel()) {
8960
9069
  return `The selected slot is ${this.confirmedSlotLabel()} and should now be visible across the viewing workflow.`;
8961
9070
  }
@@ -8984,7 +9093,81 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
8984
9093
  return !!this.viewing?.viewingDate && slot.date === this.viewing.viewingDate && slot.time === this.viewing.viewingTime;
8985
9094
  }
8986
9095
  canConfirmSlot(slot) {
8987
- return !this.viewing?.viewingDate && !!slot?.id;
9096
+ return !this.viewing?.viewingDate && !!slot?.id && !this.isAwaitingAcceptance();
9097
+ }
9098
+ canCounterSlots() {
9099
+ const code = propertyViewingStatusCode(this.viewing?.status);
9100
+ return !this.viewing?.viewingDate && code !== 'COMPLETED' && code !== 'CANCELLED' && code !== 'REJECTED';
9101
+ }
9102
+ isAwaitingAcceptance() {
9103
+ return (!this.viewing?.viewingDate &&
9104
+ (this.viewing?.slotDecisionRequired === true ||
9105
+ this.slotProposalSource() === 'ADMIN_COUNTER' ||
9106
+ propertyViewingStatusCode(this.viewing?.status) === 'COUNTERED'));
9107
+ }
9108
+ openCounterSlots() {
9109
+ this.counterSlots.set(this.createCounterSlots(this.viewing?.proposedSlots));
9110
+ this.counterMode.set(true);
9111
+ }
9112
+ cancelCounterSlots() {
9113
+ this.counterMode.set(false);
9114
+ this.counterSlots.set(this.createCounterSlots(this.viewing?.proposedSlots));
9115
+ }
9116
+ updateCounterSlot(index, slot) {
9117
+ this.counterSlots.update((current) => current.map((item, itemIndex) => itemIndex === index
9118
+ ? {
9119
+ ...item,
9120
+ ...slot,
9121
+ }
9122
+ : item));
9123
+ }
9124
+ sendCounterSlots() {
9125
+ const counterSlots = this.counterSlots()
9126
+ .map((slot) => ({
9127
+ date: slot.date?.trim?.() ?? slot.date,
9128
+ time: slot.time?.trim?.() ?? slot.time,
9129
+ }))
9130
+ .filter((slot) => slot.date && slot.time);
9131
+ if (counterSlots.length !== 3) {
9132
+ this.snackBarService.open('Provide three counter slots before sending them to the requester.');
9133
+ return;
9134
+ }
9135
+ const uniqueSlots = new Set(counterSlots.map((slot) => `${slot.date}::${slot.time}`));
9136
+ if (uniqueSlots.size !== counterSlots.length) {
9137
+ this.snackBarService.open('Each counter slot must use a different date and time.');
9138
+ return;
9139
+ }
9140
+ this.countering.set(true);
9141
+ this.propertyService.counterViewing(this.id, { counterSlots }).subscribe({
9142
+ next: (res) => {
9143
+ const nextViewing = res?.data;
9144
+ this.viewing = {
9145
+ ...this.viewing,
9146
+ ...nextViewing,
9147
+ status: nextViewing?.status ?? 'COUNTERED',
9148
+ slotProposalSource: nextViewing?.slotProposalSource ?? 'ADMIN_COUNTER',
9149
+ slotDecisionRequired: nextViewing?.slotDecisionRequired ?? true,
9150
+ counteredAt: nextViewing?.counteredAt ?? new Date().toISOString(),
9151
+ proposedSlots: nextViewing?.proposedSlots ??
9152
+ counterSlots.map((slot, index) => ({
9153
+ ...slot,
9154
+ id: this.viewing.proposedSlots[index]?.id,
9155
+ source: 'ADMIN_COUNTER',
9156
+ })),
9157
+ item: nextViewing?.item ?? this.viewing.item,
9158
+ };
9159
+ this.counterMode.set(false);
9160
+ this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
9161
+ this.snackBarService.open('Counter slots sent to the requester.');
9162
+ },
9163
+ error: (error) => {
9164
+ this.countering.set(false);
9165
+ this.snackBarService.open(error.message);
9166
+ },
9167
+ complete: () => {
9168
+ this.countering.set(false);
9169
+ },
9170
+ });
8988
9171
  }
8989
9172
  primaryMediaUrl() {
8990
9173
  return this.viewing?.item?.media?.[0]?.url || '';
@@ -9041,8 +9224,24 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
9041
9224
  .toLowerCase()
9042
9225
  .replace(/\b\w/g, (char) => char.toUpperCase());
9043
9226
  }
9227
+ slotProposalSource() {
9228
+ const explicitSource = this.viewing?.slotProposalSource;
9229
+ if (explicitSource) {
9230
+ return explicitSource.toUpperCase();
9231
+ }
9232
+ const slotSource = this.viewing?.proposedSlots?.[0]?.source;
9233
+ return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
9234
+ }
9235
+ createCounterSlots(source = []) {
9236
+ return Array.from({ length: 3 }, (_item, index) => ({
9237
+ date: source[index]?.date ?? '',
9238
+ time: source[index]?.time ?? '',
9239
+ source: source[index]?.source ?? null,
9240
+ state: source[index]?.state ?? null,
9241
+ }));
9242
+ }
9044
9243
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyManageViewingsDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
9045
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyManageViewingsDetailComponent, isStandalone: true, selector: "rolatech-property-manage-viewings-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Choose the slot to confirm</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\" i18n>\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
9244
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyManageViewingsDetailComponent, isStandalone: true, selector: "rolatech-property-manage-viewings-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\">\n @if (isAwaitingAcceptance()) {\n Waiting for requester acceptance\n } @else {\n Choose the slot to confirm\n }\n </h2>\n </div>\n\n @if (canCounterSlots()) {\n <button mat-stroked-button type=\"button\" (click)=\"openCounterSlots()\" i18n>Counter with 3 new slots</button>\n }\n </div>\n\n @if (counterMode()) {\n <div class=\"property-manage-viewings-detail__counter-panel\">\n <div class=\"property-manage-viewings-detail__counter-copy\">\n <strong i18n>Return three new slots</strong>\n <p i18n>\n Use this when the original request does not fit the diary. The requester will need to accept one of these\n returned options.\n </p>\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-grid\">\n @for (slot of counterSlots(); track $index) {\n <div class=\"property-manage-viewings-detail__counter-card\">\n <div class=\"property-manage-viewings-detail__counter-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time\n [proposedTime]=\"slot\"\n (output)=\"updateCounterSlot($index, $event)\"\n ></rolatech-property-viewing-time>\n </div>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"cancelCounterSlots()\" i18n>Cancel</button>\n <button mat-flat-button type=\"button\" [disabled]=\"countering()\" (click)=\"sendCounterSlots()\">\n @if (countering()) {\n Sending...\n } @else {\n Send counter slots\n }\n </button>\n </div>\n </div>\n }\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else if (isAwaitingAcceptance()) {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Sent for acceptance</span>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\">\n @if (isAwaitingAcceptance()) {\n The requester now needs to accept one of the returned slots. Once they do, the accepted time becomes the source\n of truth for this viewing.\n } @else {\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n }\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__counter-panel{display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem;padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-brand-color) 6%,var(--rt-raised-background, #ffffff) 94%)}.property-manage-viewings-detail__counter-copy{display:flex;flex-direction:column;gap:.35rem}.property-manage-viewings-detail__counter-copy p{margin:0;color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__counter-grid{display:grid;gap:.85rem}.property-manage-viewings-detail__counter-card{padding:.95rem 1rem .35rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-manage-viewings-detail__counter-label{margin-bottom:.5rem;color:var(--rt-text-secondary);font-size:.82rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.property-manage-viewings-detail__counter-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.75rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__counter-actions{justify-content:stretch}.property-manage-viewings-detail__counter-actions>*{flex:1 1 100%}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
9046
9245
  }
9047
9246
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyManageViewingsDetailComponent, decorators: [{
9048
9247
  type: Component,
@@ -9055,13 +9254,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
9055
9254
  Skeleton,
9056
9255
  ToolbarComponent,
9057
9256
  PricePipe,
9058
- ], template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Choose the slot to confirm</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\" i18n>\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"] }]
9257
+ FormsModule,
9258
+ PropertyViewingTimeComponent,
9259
+ ], template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\">\n @if (isAwaitingAcceptance()) {\n Waiting for requester acceptance\n } @else {\n Choose the slot to confirm\n }\n </h2>\n </div>\n\n @if (canCounterSlots()) {\n <button mat-stroked-button type=\"button\" (click)=\"openCounterSlots()\" i18n>Counter with 3 new slots</button>\n }\n </div>\n\n @if (counterMode()) {\n <div class=\"property-manage-viewings-detail__counter-panel\">\n <div class=\"property-manage-viewings-detail__counter-copy\">\n <strong i18n>Return three new slots</strong>\n <p i18n>\n Use this when the original request does not fit the diary. The requester will need to accept one of these\n returned options.\n </p>\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-grid\">\n @for (slot of counterSlots(); track $index) {\n <div class=\"property-manage-viewings-detail__counter-card\">\n <div class=\"property-manage-viewings-detail__counter-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time\n [proposedTime]=\"slot\"\n (output)=\"updateCounterSlot($index, $event)\"\n ></rolatech-property-viewing-time>\n </div>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"cancelCounterSlots()\" i18n>Cancel</button>\n <button mat-flat-button type=\"button\" [disabled]=\"countering()\" (click)=\"sendCounterSlots()\">\n @if (countering()) {\n Sending...\n } @else {\n Send counter slots\n }\n </button>\n </div>\n </div>\n }\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else if (isAwaitingAcceptance()) {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Sent for acceptance</span>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\">\n @if (isAwaitingAcceptance()) {\n The requester now needs to accept one of the returned slots. Once they do, the accepted time becomes the source\n of truth for this viewing.\n } @else {\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n }\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__counter-panel{display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem;padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-brand-color) 6%,var(--rt-raised-background, #ffffff) 94%)}.property-manage-viewings-detail__counter-copy{display:flex;flex-direction:column;gap:.35rem}.property-manage-viewings-detail__counter-copy p{margin:0;color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__counter-grid{display:grid;gap:.85rem}.property-manage-viewings-detail__counter-card{padding:.95rem 1rem .35rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-manage-viewings-detail__counter-label{margin-bottom:.5rem;color:var(--rt-text-secondary);font-size:.82rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.property-manage-viewings-detail__counter-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.75rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__counter-actions{justify-content:stretch}.property-manage-viewings-detail__counter-actions>*{flex:1 1 100%}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"] }]
9059
9260
  }] });
9060
9261
 
9061
9262
  const propertyManageViewingsRoutes = [
9062
9263
  {
9063
9264
  path: '',
9064
- loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-C37LmOC0.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
9265
+ loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-Bvxmxhbu.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
9065
9266
  },
9066
9267
  {
9067
9268
  path: ':id',
@@ -9872,7 +10073,7 @@ class PropertyMarketViewingRequest extends BaseComponent {
9872
10073
  this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
9873
10074
  }
9874
10075
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
9875
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingRequest, isStandalone: true, selector: "rolatech-letting-viewing-request", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three\n viewing options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.annualIncome\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Budget - per month</mat-label>\n <span matTextPrefix>\u00A3&nbsp;</span>\n <input matInput type=\"number\" min=\"0\" [(ngModel)]=\"viewing.monthlyBudget\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Move in date</mat-label>\n <input\n matInput\n [matDatepicker]=\"moveInDatePicker\"\n [min]=\"minMoveInDate\"\n [ngModel]=\"moveInDate\"\n (dateChange)=\"setMoveInDate($event.value)\"\n (focus)=\"moveInDatePicker.open()\"\n (click)=\"moveInDatePicker.open()\"\n readonly\n />\n <mat-datepicker-toggle matIconSuffix [for]=\"moveInDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #moveInDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__slot-section\">\n <div class=\"property-market-viewing-request__slot-head\">\n <h3 i18n>Preferred viewing slots</h3>\n <p i18n>Choose three options across at least two different days so the seller can confirm quickly.</p>\n </div>\n\n <div class=\"property-market-viewing-request__slots\">\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"property-market-viewing-request__slot-card\">\n <div class=\"property-market-viewing-request__slot-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n </div>\n }\n </div>\n </div>\n\n <button mat-flat-button class=\"property-market-viewing-request__submit\" (click)=\"sendRequest()\" [disabled]=\"sending\">\n <span class=\"property-market-viewing-request__submit-content\">\n @if (sending) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span i18n>{{ sending ? 'Sending request...' : 'Send request' }}</span>\n </span>\n </button>\n </section>\n </main>\n\n <aside class=\"property-market-viewing-request__side\">\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <div class=\"property-market-viewing-request__summary-head\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-viewing-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-viewing-request__panel property-market-viewing-request__panel--summary\">\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>What happens next</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Updates appear in your viewings inbox</h2>\n <p class=\"property-market-viewing-request__description\" i18n>\n Once admin confirms a slot, you will see the approved time in the viewing detail page and can jump back to this\n property from the breadcrumb trail.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-viewing-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-viewing-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-viewing-request__hero,.property-market-viewing-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-viewing-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__eyebrow,.property-market-viewing-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-viewing-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-viewing-request__description,.property-market-viewing-request__hint,.property-market-viewing-request__slot-head p{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-viewing-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-viewing-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-viewing-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-viewing-request__fact-label,.property-market-viewing-request__slot-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-viewing-request__content,.property-market-viewing-request__main,.property-market-viewing-request__side,.property-market-viewing-request__slots{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-request__panel{padding:1.25rem}.property-market-viewing-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-viewing-request__panel-head,.property-market-viewing-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-viewing-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-viewing-request__grid{display:grid;gap:.85rem}.property-market-viewing-request__slot-section{display:flex;flex-direction:column;gap:1rem;margin-top:.5rem}.property-market-viewing-request__slot-head h3{margin:0 0 .3rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-request__slot-card{display:flex;flex-direction:column;gap:.6rem;padding:1rem;border-radius:1.15rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-additive-background, var(--rt-base-background, #ffffff)) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-request__submit{min-height:3rem;border-radius:9999px}.property-market-viewing-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-viewing-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-viewing-request{padding:1.25rem}.property-market-viewing-request__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:1100px){.property-market-viewing-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output", "select"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
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" }] });
9876
10077
  }
9877
10078
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, decorators: [{
9878
10079
  type: Component,
@@ -10360,72 +10561,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
10360
10561
  ], template: "<rolatech-property-workspace-shell\n eyebrow=\"Agent workspace\"\n title=\"Listing viewings\"\n description=\"Keep buyer appointments organized across your managed listings, review the latest requests, and move straight into viewing detail when something changes.\"\n [stats]=\"stats()\"\n>\n <nav workspace-filters class=\"property-listing-viewing-index__status-nav\" aria-label=\"Listing viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-listing-viewing-index__status-link\"\n [class.property-listing-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-listing-viewing-index__list\">\n @if (loading) {\n @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-listing-viewing-index__placeholder\"></div>\n }\n } @else if (viewings().length) {\n @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [viewing]=\"item\"></rolatech-property-viewing-item>\n }\n } @else {\n <div class=\"property-listing-viewing-index__empty\">\n <h3 i18n>No viewing activity yet</h3>\n <p i18n>Incoming listing appointments will appear here once buyers or partner agents send requests.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</rolatech-property-workspace-shell>\n", styles: [":host{display:block;padding:1rem}.property-listing-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-listing-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.property-listing-viewing-index__status-link:hover,.property-listing-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-listing-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-listing-viewing-index__placeholder,.property-listing-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-listing-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-listing-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-listing-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){:host{padding:1.25rem}}\n"] }]
10361
10562
  }] });
10362
10563
 
10363
- class PropertyListingViewingDetail extends BaseComponent {
10364
- propertyService = inject(PropertyService);
10365
- authUserService = inject(AuthUserService);
10366
- viewing;
10367
- name = '';
10368
- agent;
10369
- viewingTimeConfirmed = false;
10370
- selectedSlotId = '';
10371
- ngOnInit() {
10372
- this.getViewing();
10373
- }
10374
- getViewing() {
10375
- this.propertyService.getViewing(this.id).subscribe({
10376
- next: (res) => {
10377
- this.viewing = res.data;
10378
- this.name = this.viewing.firstName + ', ' + this.viewing.lastName;
10379
- if (this.viewing.agentId) {
10380
- this.getAgentPublicInfo(this.viewing.agentId);
10381
- }
10382
- this.viewingTimeConfirmed = this.viewing.viewingDate ? true : false;
10383
- },
10384
- });
10385
- }
10386
- getAgentPublicInfo(agentId) {
10387
- this.authUserService.getPublicUserInfo(agentId).subscribe({
10388
- next: (res) => {
10389
- this.agent = res;
10390
- },
10391
- });
10392
- }
10393
- findConfirmedViewingTime() {
10394
- const viewingProposedSlot = this.viewing.proposedSlots.find((item) => {
10395
- return item.date === this.viewing.viewingDate && item.time === this.viewing.viewingTime;
10396
- });
10397
- this.selectedSlotId = viewingProposedSlot.id;
10398
- }
10399
- confirmViewingTime(item) {
10400
- const options = {
10401
- title: 'Confirm viewing time',
10402
- component: PropertyViewingConfirmationComponent,
10403
- data: {
10404
- proposedTime: item,
10405
- },
10406
- };
10407
- this.dialogService.open(options);
10408
- this.dialogService.confirmed().subscribe({
10409
- next: (result) => {
10410
- if (result) {
10411
- this.propertyService.confirmViewing(this.id, item.id).subscribe({
10412
- next: (res) => {
10413
- this.viewingTimeConfirmed = true;
10414
- },
10415
- });
10416
- }
10417
- },
10418
- });
10419
- }
10420
- statusLabel(status) {
10421
- return propertyViewingStatusLabel(status);
10422
- }
10423
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, deps: null, target: i0.ɵɵFactoryTarget.Component });
10424
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingViewingDetail, isStandalone: true, selector: "rolatech-property-listing-viewing-detail", usesInheritance: true, ngImport: i0, template: "@if (viewing) {\n<rolatech-toolbar [title]=\"statusLabel(viewing.status)\" large link=\"../\"></rolatech-toolbar>\n<div class=\"px-4\">\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewer</div>\n <hr class=\"mb-2\" />\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"name\"></rolatech-rich-label>\n </div>\n </div>\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) { @if (item.date === viewing.viewingDate && item.time ===\n viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n } } @if (!viewingTimeConfirmed) {\n <div class=\"ml-3\"><button mat-flat-button (click)=\"confirmViewingTime(item)\" i18n>Confirm</button></div>\n }\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n @if (viewing.item) {\n <div class=\"flex items-center py-2\">\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @if (viewing.item.media[0]?.url) {\n @defer {\n <rolatech-thumbnail [src]=\"viewing.item.media[0]?.url || ''\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n } @else {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing.item.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewing agent</div>\n <hr class=\"mb-2\" />\n @if (agent) {\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"agent.name\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Email\" [title]=\"agent.email\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Phone\" [title]=\"agent.phone\"></rolatech-rich-label>\n </div>\n }\n </div>\n</div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: RichLabelComponent, selector: "rolatech-rich-label", inputs: ["label", "title"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
10564
+ class PropertyListingViewingDetail {
10565
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
10566
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyListingViewingDetail, isStandalone: true, selector: "rolatech-property-listing-viewing-detail", ngImport: i0, template: "<rolatech-property-manage-viewings-detail></rolatech-property-manage-viewings-detail>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyManageViewingsDetailComponent, selector: "rolatech-property-manage-viewings-detail" }] });
10425
10567
  }
10426
10568
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, decorators: [{
10427
10569
  type: Component,
10428
- args: [{ selector: 'rolatech-property-listing-viewing-detail', imports: [CommonModule, ToolbarComponent, RichLabelComponent, ThumbnailComponent, MatButtonModule, PricePipe], template: "@if (viewing) {\n<rolatech-toolbar [title]=\"statusLabel(viewing.status)\" large link=\"../\"></rolatech-toolbar>\n<div class=\"px-4\">\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewer</div>\n <hr class=\"mb-2\" />\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"name\"></rolatech-rich-label>\n </div>\n </div>\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) { @if (item.date === viewing.viewingDate && item.time ===\n viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n } } @if (!viewingTimeConfirmed) {\n <div class=\"ml-3\"><button mat-flat-button (click)=\"confirmViewingTime(item)\" i18n>Confirm</button></div>\n }\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n @if (viewing.item) {\n <div class=\"flex items-center py-2\">\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @if (viewing.item.media[0]?.url) {\n @defer {\n <rolatech-thumbnail [src]=\"viewing.item.media[0]?.url || ''\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n } @else {\n <div class=\"bg-(--rt-raised-background) h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing.item.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Viewing agent</div>\n <hr class=\"mb-2\" />\n @if (agent) {\n <div class=\"flex flex-col md:flex-row gap-1 md:gap-3\">\n <rolatech-rich-label label=\"Name\" [title]=\"agent.name\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Email\" [title]=\"agent.email\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Phone\" [title]=\"agent.phone\"></rolatech-rich-label>\n </div>\n }\n </div>\n</div>\n}\n" }]
10570
+ args: [{ selector: 'rolatech-property-listing-viewing-detail', imports: [CommonModule, PropertyManageViewingsDetailComponent], template: "<rolatech-property-manage-viewings-detail></rolatech-property-manage-viewings-detail>\n" }]
10429
10571
  }] });
10430
10572
 
10431
10573
  const propertyListingViewingRoutes = [
@@ -10475,7 +10617,7 @@ class PropertyListingOfferDetail extends BaseComponent {
10475
10617
  this.propertyOfferService.getOffer(this.id).subscribe({
10476
10618
  next: (res) => {
10477
10619
  this.offer = res.data;
10478
- // this.name = this.isRental()? this.offer.rentalTerms?.tenancyLengthMonths
10620
+ this.name = this.applicantName();
10479
10621
  this.getProperty(this.offer.propertyId);
10480
10622
  },
10481
10623
  });
@@ -10560,7 +10702,9 @@ class PropertyListingOfferDetail extends BaseComponent {
10560
10702
  }
10561
10703
  });
10562
10704
  }
10563
- counter() { }
10705
+ counter() {
10706
+ this.f.enterEdit();
10707
+ }
10564
10708
  rfPassed() {
10565
10709
  const dialogRef = this.dialog.open(AcceptDialogComponent, {
10566
10710
  width: '400px',
@@ -10671,19 +10815,58 @@ class PropertyListingOfferDetail extends BaseComponent {
10671
10815
  },
10672
10816
  });
10673
10817
  }
10818
+ applicantName() {
10819
+ const primaryTenant = this.offer?.tenants?.[0];
10820
+ return primaryTenant?.fullName || this.offer?.sale?.buyerName || this.name || 'Applicant';
10821
+ }
10822
+ applicantContactSummary() {
10823
+ if (!this.offer) {
10824
+ return 'Contact details appear here once the offer loads.';
10825
+ }
10826
+ const email = this.applicantEmail();
10827
+ const phone = this.applicantPhone();
10828
+ if (email && phone) {
10829
+ return `${email} · ${phone}`;
10830
+ }
10831
+ return email || phone || 'Contact details not shared';
10832
+ }
10833
+ agentName() {
10834
+ return this.agent?.name || 'Assigned agent';
10835
+ }
10836
+ agentEmail() {
10837
+ return this.agent?.email || 'Not shared';
10838
+ }
10839
+ agentPhone() {
10840
+ return this.agent?.phone || 'Not shared';
10841
+ }
10842
+ propertyMediaUrl() {
10843
+ return this.property?.media?.[0]?.url || '';
10844
+ }
10845
+ applicantEmail() {
10846
+ return this.offer?.tenants?.[0]?.email || this.offer?.sale?.email || '';
10847
+ }
10848
+ applicantPhone() {
10849
+ return this.offer?.tenants?.[0]?.phone || this.offer?.sale?.phone || '';
10850
+ }
10851
+ targetDate() {
10852
+ return this.offer?.rentalTerms?.moveInDate || this.offer?.sale?.proposedExchangeDate || null;
10853
+ }
10674
10854
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingOfferDetail, deps: null, target: i0.ɵɵFactoryTarget.Component });
10675
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingOfferDetail, isStandalone: true, selector: "rolatech-property-listing-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], usesInheritance: true, ngImport: i0, template: "@if (offer) {\n<rolatech-toolbar [title]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4 h-fit\">\n <!-- Primary negotiation actions -->\n <rolatech-offer-negotiation-section />\n\n <rolatech-offer-item-card />\n\n <!-- Tenants card -->\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-(--rt-border-color) bg-(--rt-surface-2) p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <!-- Offer references -->\n <rolatech-offer-reference-provider />\n\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">Counter Offer</button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n</mat-menu>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferReferenceProvider, selector: "rolatech-offer-reference-provider", inputs: ["provider", "providerOther"], outputs: ["providerChange", "providerOtherChange"] }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferInternalNoteCard, selector: "rolatech-offer-internal-note-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }] });
10855
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingOfferDetail, isStandalone: true, selector: "rolatech-property-listing-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], usesInheritance: true, ngImport: i0, template: "@if (offer) {\n<div class=\"property-listing-offer-detail\">\n <rolatech-toolbar [title]=\"'Offer detail'\" [subtitle]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button type=\"button\" (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <section class=\"property-listing-offer-detail__hero\">\n <div class=\"property-listing-offer-detail__hero-copy\">\n <span class=\"property-listing-offer-detail__eyebrow\" i18n>Agent workspace</span>\n\n <div class=\"property-listing-offer-detail__hero-heading\">\n <h1 class=\"property-listing-offer-detail__title\">{{ property.title || 'Offer detail' }}</h1>\n <span class=\"property-listing-offer-detail__status\">{{ statusLabel() }}</span>\n </div>\n\n <p class=\"property-listing-offer-detail__intro\" i18n>\n Keep negotiation, billing, referencing, and applicant coordination aligned from one offer workspace.\n </p>\n\n <div class=\"property-listing-offer-detail__fact-grid\">\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Offer amount</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.item.amount | price }}</strong>\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ targetDate() ? (targetDate() | date: 'mediumDate') : 'Not shared' }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Invoice option</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ invoiceOptionLabel[invoiceOptionDraft() || offer.invoiceOption] }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Applicants</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.tenants?.length ?? 1 }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__hero-side\">\n <article class=\"property-listing-offer-detail__property-card\">\n <div class=\"property-listing-offer-detail__media\">\n @if (propertyMediaUrl()) { @defer {\n <rolatech-thumbnail [src]=\"propertyMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-listing-offer-detail__media-fallback\"></div>\n } } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-listing-offer-detail__property-copy\">\n <div class=\"property-listing-offer-detail__property-price\">{{ (property.price || offer.item.amount) | price }}</div>\n @if (property) {\n <div class=\"property-listing-offer-detail__property-facts\">\n <span>{{ property.bedrooms }} bed</span>\n <span>{{ property.bathrooms }} bath</span>\n <span>{{ property.receptions }} reception</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"property-listing-offer-detail__summary-card\">\n <span class=\"property-listing-offer-detail__summary-label\" i18n>Applicant</span>\n <strong class=\"property-listing-offer-detail__summary-value\">{{ applicantName() }}</strong>\n <span class=\"property-listing-offer-detail__summary-contact\">{{ applicantContactSummary() }}</span>\n </article>\n </div>\n </section>\n\n <div class=\"property-listing-offer-detail__content\">\n <main class=\"property-listing-offer-detail__main\">\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </main>\n\n <aside class=\"property-listing-offer-detail__side\">\n <div class=\"property-listing-offer-detail__sticky\">\n <section class=\"property-listing-offer-detail__panel\">\n <div class=\"property-listing-offer-detail__panel-head\">\n <div>\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Billing</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move-in invoice handling</h2>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <p class=\"property-listing-offer-detail__summary-note\" i18n>\n Choose whether security deposit and first rent land on a combined invoice or separate invoices.\n </p>\n\n <div class=\"property-listing-offer-detail__radio-grid\">\n <mat-radio-group\n class=\"block\"\n [value]=\"invoiceOptionDraft() || offer.invoiceOption\"\n (change)=\"setInvoiceOption($event.value)\"\n >\n <div class=\"grid gap-3\">\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with security deposit and first rent on separate lines.\n </div>\n </div>\n </div>\n </label>\n\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Issue separate invoices so move-in payments can be tracked independently.\n </div>\n </div>\n </div>\n </label>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <rolatech-offer-reference-provider />\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Applicant contact</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ applicantName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ applicantEmail() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ applicantPhone() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>User id</span>\n <strong>{{ offer.userId || 'Not attached' }}</strong>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__action-row\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantEmail()\" (click)=\"emailApplicant(applicantEmail())\">\n Email\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantPhone()\" (click)=\"callApplicant(applicantPhone())\">\n Call\n </button>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Listing agent</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ agentName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Agent id</span>\n <strong>{{ offer.agentId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Quick actions</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move the negotiation forward</h2>\n\n <div class=\"property-listing-offer-detail__stack-actions\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button type=\"button\" (click)=\"rfPassed()\">References passed</button>\n <button mat-stroked-button type=\"button\" (click)=\"rfFailed()\">References failed</button>\n }\n </div>\n </section>\n </div>\n </aside>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">\n Counter Offer\n </button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n </mat-menu>\n</div>\n}\n", styles: [":host{display:block}.property-listing-offer-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%),var(--rt-base-background, #ffffff))}.property-listing-offer-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-listing-offer-detail__hero-copy,.property-listing-offer-detail__hero-side,.property-listing-offer-detail__main,.property-listing-offer-detail__side,.property-listing-offer-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-listing-offer-detail__eyebrow,.property-listing-offer-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-listing-offer-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-listing-offer-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-listing-offer-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.82rem;font-weight:700}.property-listing-offer-detail__intro,.property-listing-offer-detail__summary-note,.property-listing-offer-detail__summary-contact{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-listing-offer-detail__intro{color:var(--rt-text-primary);font-size:1.02rem;font-weight:700}.property-listing-offer-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-listing-offer-detail__fact,.property-listing-offer-detail__summary-card,.property-listing-offer-detail__property-card,.property-listing-offer-detail__panel,.property-listing-offer-detail__radio-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-offer-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-listing-offer-detail__fact-label,.property-listing-offer-detail__summary-label,.property-listing-offer-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-listing-offer-detail__fact-value,.property-listing-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-listing-offer-detail__property-card{display:flex;flex-direction:column;gap:.95rem;padding:1rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-listing-offer-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-listing-offer-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-listing-offer-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-listing-offer-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-listing-offer-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-listing-offer-detail__summary-card,.property-listing-offer-detail__panel{display:flex;flex-direction:column;gap:.4rem;padding:1rem 1.05rem}.property-listing-offer-detail__summary-value,.property-listing-offer-detail__summary-contact,.property-listing-offer-detail__summary-list strong{overflow-wrap:anywhere;word-break:break-word}.property-listing-offer-detail__content{display:grid;gap:1rem}.property-listing-offer-detail__panel{border-radius:1.5rem;padding:1.25rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-listing-offer-detail__panel-head{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-listing-offer-detail__radio-grid{margin-top:.5rem}.property-listing-offer-detail__radio-card{display:block;padding:.95rem 1rem}.property-listing-offer-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-listing-offer-detail__action-row,.property-listing-offer-detail__stack-actions{display:grid;gap:.75rem}@media(min-width:640px){.property-listing-offer-detail__action-row{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:768px){.property-listing-offer-detail{padding:1.25rem}.property-listing-offer-detail__hero{grid-template-columns:minmax(0,1.5fr) minmax(18rem,.9fr);align-items:stretch;padding:1.5rem}.property-listing-offer-detail__hero-heading,.property-listing-offer-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-listing-offer-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-listing-offer-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-listing-offer-detail__fact-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferReferenceProvider, selector: "rolatech-offer-reference-provider", inputs: ["provider", "providerOther"], outputs: ["providerChange", "providerOtherChange"] }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferInternalNoteCard, selector: "rolatech-offer-internal-note-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
10676
10856
  }
10677
10857
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingOfferDetail, decorators: [{
10678
10858
  type: Component,
10679
10859
  args: [{ selector: 'rolatech-property-listing-offer-detail', imports: [
10680
10860
  CommonModule,
10681
10861
  ToolbarComponent,
10862
+ ThumbnailComponent,
10863
+ ImagePlaceholderComponent,
10682
10864
  MatButtonModule,
10683
10865
  MatIcon,
10684
10866
  MatMenuModule,
10685
10867
  MatRadioModule,
10686
10868
  FormsModule,
10869
+ PricePipe,
10687
10870
  OfferRentalTermsCard,
10688
10871
  OfferTenantsAccordion,
10689
10872
  OfferItemCard,
@@ -10694,7 +10877,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
10694
10877
  OfferCounterLatestCard,
10695
10878
  OfferCounterPreviousCard,
10696
10879
  OfferNegotiationSection,
10697
- ], providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], template: "@if (offer) {\n<rolatech-toolbar [title]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4 h-fit\">\n <!-- Primary negotiation actions -->\n <rolatech-offer-negotiation-section />\n\n <rolatech-offer-item-card />\n\n <!-- Tenants card -->\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-(--rt-border-color) bg-(--rt-surface-2) p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <!-- Offer references -->\n <rolatech-offer-reference-provider />\n\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">Counter Offer</button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n</mat-menu>\n}\n" }]
10880
+ ], providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], template: "@if (offer) {\n<div class=\"property-listing-offer-detail\">\n <rolatech-toolbar [title]=\"'Offer detail'\" [subtitle]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button type=\"button\" (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <section class=\"property-listing-offer-detail__hero\">\n <div class=\"property-listing-offer-detail__hero-copy\">\n <span class=\"property-listing-offer-detail__eyebrow\" i18n>Agent workspace</span>\n\n <div class=\"property-listing-offer-detail__hero-heading\">\n <h1 class=\"property-listing-offer-detail__title\">{{ property.title || 'Offer detail' }}</h1>\n <span class=\"property-listing-offer-detail__status\">{{ statusLabel() }}</span>\n </div>\n\n <p class=\"property-listing-offer-detail__intro\" i18n>\n Keep negotiation, billing, referencing, and applicant coordination aligned from one offer workspace.\n </p>\n\n <div class=\"property-listing-offer-detail__fact-grid\">\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Offer amount</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.item.amount | price }}</strong>\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ targetDate() ? (targetDate() | date: 'mediumDate') : 'Not shared' }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Invoice option</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ invoiceOptionLabel[invoiceOptionDraft() || offer.invoiceOption] }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Applicants</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.tenants?.length ?? 1 }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__hero-side\">\n <article class=\"property-listing-offer-detail__property-card\">\n <div class=\"property-listing-offer-detail__media\">\n @if (propertyMediaUrl()) { @defer {\n <rolatech-thumbnail [src]=\"propertyMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-listing-offer-detail__media-fallback\"></div>\n } } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-listing-offer-detail__property-copy\">\n <div class=\"property-listing-offer-detail__property-price\">{{ (property.price || offer.item.amount) | price }}</div>\n @if (property) {\n <div class=\"property-listing-offer-detail__property-facts\">\n <span>{{ property.bedrooms }} bed</span>\n <span>{{ property.bathrooms }} bath</span>\n <span>{{ property.receptions }} reception</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"property-listing-offer-detail__summary-card\">\n <span class=\"property-listing-offer-detail__summary-label\" i18n>Applicant</span>\n <strong class=\"property-listing-offer-detail__summary-value\">{{ applicantName() }}</strong>\n <span class=\"property-listing-offer-detail__summary-contact\">{{ applicantContactSummary() }}</span>\n </article>\n </div>\n </section>\n\n <div class=\"property-listing-offer-detail__content\">\n <main class=\"property-listing-offer-detail__main\">\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </main>\n\n <aside class=\"property-listing-offer-detail__side\">\n <div class=\"property-listing-offer-detail__sticky\">\n <section class=\"property-listing-offer-detail__panel\">\n <div class=\"property-listing-offer-detail__panel-head\">\n <div>\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Billing</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move-in invoice handling</h2>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <p class=\"property-listing-offer-detail__summary-note\" i18n>\n Choose whether security deposit and first rent land on a combined invoice or separate invoices.\n </p>\n\n <div class=\"property-listing-offer-detail__radio-grid\">\n <mat-radio-group\n class=\"block\"\n [value]=\"invoiceOptionDraft() || offer.invoiceOption\"\n (change)=\"setInvoiceOption($event.value)\"\n >\n <div class=\"grid gap-3\">\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with security deposit and first rent on separate lines.\n </div>\n </div>\n </div>\n </label>\n\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Issue separate invoices so move-in payments can be tracked independently.\n </div>\n </div>\n </div>\n </label>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <rolatech-offer-reference-provider />\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Applicant contact</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ applicantName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ applicantEmail() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ applicantPhone() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>User id</span>\n <strong>{{ offer.userId || 'Not attached' }}</strong>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__action-row\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantEmail()\" (click)=\"emailApplicant(applicantEmail())\">\n Email\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantPhone()\" (click)=\"callApplicant(applicantPhone())\">\n Call\n </button>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Listing agent</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ agentName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Agent id</span>\n <strong>{{ offer.agentId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Quick actions</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move the negotiation forward</h2>\n\n <div class=\"property-listing-offer-detail__stack-actions\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button type=\"button\" (click)=\"rfPassed()\">References passed</button>\n <button mat-stroked-button type=\"button\" (click)=\"rfFailed()\">References failed</button>\n }\n </div>\n </section>\n </div>\n </aside>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">\n Counter Offer\n </button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n </mat-menu>\n</div>\n}\n", styles: [":host{display:block}.property-listing-offer-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%),var(--rt-base-background, #ffffff))}.property-listing-offer-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-listing-offer-detail__hero-copy,.property-listing-offer-detail__hero-side,.property-listing-offer-detail__main,.property-listing-offer-detail__side,.property-listing-offer-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-listing-offer-detail__eyebrow,.property-listing-offer-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-listing-offer-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-listing-offer-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-listing-offer-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.82rem;font-weight:700}.property-listing-offer-detail__intro,.property-listing-offer-detail__summary-note,.property-listing-offer-detail__summary-contact{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-listing-offer-detail__intro{color:var(--rt-text-primary);font-size:1.02rem;font-weight:700}.property-listing-offer-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-listing-offer-detail__fact,.property-listing-offer-detail__summary-card,.property-listing-offer-detail__property-card,.property-listing-offer-detail__panel,.property-listing-offer-detail__radio-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-offer-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-listing-offer-detail__fact-label,.property-listing-offer-detail__summary-label,.property-listing-offer-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-listing-offer-detail__fact-value,.property-listing-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-listing-offer-detail__property-card{display:flex;flex-direction:column;gap:.95rem;padding:1rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-listing-offer-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-listing-offer-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-listing-offer-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-listing-offer-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-listing-offer-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-listing-offer-detail__summary-card,.property-listing-offer-detail__panel{display:flex;flex-direction:column;gap:.4rem;padding:1rem 1.05rem}.property-listing-offer-detail__summary-value,.property-listing-offer-detail__summary-contact,.property-listing-offer-detail__summary-list strong{overflow-wrap:anywhere;word-break:break-word}.property-listing-offer-detail__content{display:grid;gap:1rem}.property-listing-offer-detail__panel{border-radius:1.5rem;padding:1.25rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-listing-offer-detail__panel-head{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-listing-offer-detail__radio-grid{margin-top:.5rem}.property-listing-offer-detail__radio-card{display:block;padding:.95rem 1rem}.property-listing-offer-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-listing-offer-detail__action-row,.property-listing-offer-detail__stack-actions{display:grid;gap:.75rem}@media(min-width:640px){.property-listing-offer-detail__action-row{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:768px){.property-listing-offer-detail{padding:1.25rem}.property-listing-offer-detail__hero{grid-template-columns:minmax(0,1.5fr) minmax(18rem,.9fr);align-items:stretch;padding:1.5rem}.property-listing-offer-detail__hero-heading,.property-listing-offer-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-listing-offer-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-listing-offer-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-listing-offer-detail__fact-grid{grid-template-columns:1fr}}\n"] }]
10698
10881
  }] });
10699
10882
 
10700
10883
  class PropertyListingOfferIndex extends BaseComponent {
@@ -11359,4 +11542,4 @@ function provideAngularPropertyFeature() {
11359
11542
  */
11360
11543
 
11361
11544
  export { PropertyScope as A, PropertyViewingStatus as B, PropertyViewerCategory as C, propertyViewingStatusCode as D, propertyViewingStatusLabel as E, PropertyOfferTimelineStatus as F, PropertyOfferType as G, PropertyOfferStatus as H, EmploymentStatus as I, ResidencyStatus as J, PropertyApplicantType as K, AdverseCreditStatus as L, Market as M, BedroomRange as N, PriceRange as O, PropertyUtil as P, RentFrequency as R, ViewingTime as V, PropertyViewType as a, PropertyManageItemSkeleton as b, PropertyViewingItemComponent as c, propertyManageOffersRoutes as d, propertyRoutes as e, featureManageRoutes as f, propertyManageRoutes as g, propertyManageViewingsRoutes as h, propertyMarketRoutes as i, propertyListingViewingRoutes as j, propertyListingOfferRoutes as k, propertyAgentOverviewRoutes as l, propertyManageOverviewRoutes as m, propertyListingRoutes as n, provideAngularProperty as o, propertyReviewRoutes as p, provideAngularPropertyFeature as q, PropertyActionsComponent as r, PropertyItemComponent as s, tenantManageRoutes as t, PropertyPricingComponent as u, PropertyPriceType as v, PropertyVideoProvider as w, PropertyStatus as x, PropertyType as y, PropertyInventoryStatus as z };
11362
- //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-DSfebWFu.mjs.map
11545
+ //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-BipC43h_.mjs.map