@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.
- package/fesm2022/{rolatech-angular-property-property-index.component-De4homi3.mjs → rolatech-angular-property-property-index.component-CbvPpms7.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-index.component-De4homi3.mjs.map → rolatech-angular-property-property-index.component-CbvPpms7.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-C37LmOC0.mjs → rolatech-angular-property-property-manage-viewings-index.component-Bvxmxhbu.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-C37LmOC0.mjs.map → rolatech-angular-property-property-manage-viewings-index.component-Bvxmxhbu.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-rolatech-angular-property-DSfebWFu.mjs → rolatech-angular-property-rolatech-angular-property-BipC43h_.mjs} +267 -84
- package/fesm2022/rolatech-angular-property-rolatech-angular-property-BipC43h_.mjs.map +1 -0
- package/fesm2022/rolatech-angular-property.mjs +1 -1
- package/package.json +6 -6
- package/themes/_default.scss +1 -1
- package/types/rolatech-angular-property.d.ts +6 -0
- package/fesm2022/rolatech-angular-property-rolatech-angular-property-DSfebWFu.mjs.map +0 -1
|
@@ -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
|
-
|
|
4115
|
-
this.
|
|
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"
|
|
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 [
|
|
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 [
|
|
4146
|
-
}], propDecorators: { output: [{ type: i0.Output, args: ["output"] }],
|
|
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 </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 </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 </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 </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-
|
|
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-
|
|
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 </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 </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 </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 </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
|
|
10364
|
-
|
|
10365
|
-
|
|
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,
|
|
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
|
-
|
|
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-
|
|
11545
|
+
//# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-BipC43h_.mjs.map
|