@rolatech/angular-property 20.3.1-beta.3 → 20.3.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{rolatech-angular-property-property-index.component-De4homi3.mjs → rolatech-angular-property-property-index.component-CLCJ78vg.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-index.component-De4homi3.mjs.map → rolatech-angular-property-property-index.component-CLCJ78vg.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-C37LmOC0.mjs → rolatech-angular-property-property-manage-viewings-index.component-Dx9AXFNI.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-C37LmOC0.mjs.map → rolatech-angular-property-property-manage-viewings-index.component-Dx9AXFNI.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-rolatech-angular-property-DSfebWFu.mjs → rolatech-angular-property-rolatech-angular-property-zL_Uw36W.mjs} +736 -440
- package/fesm2022/rolatech-angular-property-rolatech-angular-property-zL_Uw36W.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
|
@@ -11,9 +11,9 @@ import { MatTableModule } from '@angular/material/table';
|
|
|
11
11
|
import * as i1$1 from '@angular/router';
|
|
12
12
|
import { RouterLink, RouterModule, ActivatedRoute, Router, RouterLinkActive, RouterOutlet } from '@angular/router';
|
|
13
13
|
import { ThumbnailComponent, ImagePlaceholderComponent, Skeleton, ToolbarComponent, Loading, ImagePreviewDialogComponent, BaseComponent, AcceptDialogComponent, RejectDialogComponent, RichLabelComponent, AngularComponentsModule, ConfirmationDialogComponent, TabsComponent, TabComponent, EmptyComponent, SearchBar, ContainerComponent, PageCollectionShellComponent, ListComponent, MediaListComponent, MediaListItemComponent, InputComponent, SpinnerComponent } from '@rolatech/angular-components';
|
|
14
|
-
import { PropertyService, FeatureService, offerStatusLabel, PropertyOfferService, PropertyOfferCounterService, InvoiceService, FloorplanService, PropertyHighlightsService, TimeZoneService, PropertyOfferStatus as PropertyOfferStatus$1,
|
|
14
|
+
import { PropertyService, FeatureService, offerStatusLabel, PropertyOfferService, PropertyOfferCounterService, InvoiceService, FloorplanService, PropertyHighlightsService, TimeZoneService, PropertyOfferStatus as PropertyOfferStatus$1, SnackBarService, TitleService, PropertySearchService, DialogService } from '@rolatech/angular-services';
|
|
15
15
|
import * as i1 from '@angular/common';
|
|
16
|
-
import { CommonModule, NgClass, isPlatformBrowser, ViewportScroller,
|
|
16
|
+
import { CommonModule, NgClass, isPlatformBrowser, ViewportScroller, KeyValuePipe } from '@angular/common';
|
|
17
17
|
import { PricePipe, APP_CONFIG, AngularCommonModule, OptionsFormatPipe, DecimalDirective, DurationPipe, SafeUrlPipe } from '@rolatech/angular-common';
|
|
18
18
|
import * as i2 from '@angular/material/icon';
|
|
19
19
|
import { MatIconModule, MatIcon } from '@angular/material/icon';
|
|
@@ -50,6 +50,7 @@ import { MatProgressSpinnerModule, MatProgressSpinner } from '@angular/material/
|
|
|
50
50
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
51
51
|
import * as i4$1 from '@angular/cdk/text-field';
|
|
52
52
|
import { TextFieldModule } from '@angular/cdk/text-field';
|
|
53
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
53
54
|
import * as i2$5 from '@angular/material/expansion';
|
|
54
55
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
55
56
|
import { MatCardModule } from '@angular/material/card';
|
|
@@ -127,6 +128,7 @@ var PropertyScope;
|
|
|
127
128
|
var PropertyViewingStatus;
|
|
128
129
|
(function (PropertyViewingStatus) {
|
|
129
130
|
PropertyViewingStatus["PENDING"] = "Pending";
|
|
131
|
+
PropertyViewingStatus["COUNTERED"] = "Countered";
|
|
130
132
|
PropertyViewingStatus["APPROVED"] = "Approved";
|
|
131
133
|
PropertyViewingStatus["REJECTED"] = "Rejected";
|
|
132
134
|
PropertyViewingStatus["CANCELLED"] = "Cancelled";
|
|
@@ -1137,7 +1139,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1137
1139
|
], template: "<rolatech-toolbar title=\"Offers\">\n <div class=\"flex items-center gap-2\">\n <button mat-icon-button (click)=\"toggleSearch()\">\n <mat-icon>search</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"p-2\">\n <rolatech-search-bar\n [(show)]=\"open\"\n placeholder=\"Search offer by offer id or address\"\n #searchBar\n (search)=\"searchByText($event)\"\n (close)=\"onCloseSearch()\"\n ></rolatech-search-bar>\n</div>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n</rolatech-tabs>\n@if (loading) {\n <div class=\"divide-y divide-(--rt-10-percent-layer) flex flex-col px-3\">\n @for (row of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n }\n </div>\n} @else {\n <div class=\"space-y-2 p-2\">\n @if (offers() && offers().length > 0) {\n @for (item of offers(); track item) {\n <rolatech-property-offer-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [offer]=\"item\"></rolatech-property-offer-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </div>\n}\n<mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n>\n</mat-paginator>\n" }]
|
|
1138
1140
|
}] });
|
|
1139
1141
|
|
|
1140
|
-
const MY_FORMATS$
|
|
1142
|
+
const MY_FORMATS$7 = {
|
|
1141
1143
|
parse: {
|
|
1142
1144
|
dateInput: 'YYYY-MM-DD',
|
|
1143
1145
|
},
|
|
@@ -1162,7 +1164,7 @@ class OfferEdit extends BaseComponent {
|
|
|
1162
1164
|
useClass: MomentDateAdapter,
|
|
1163
1165
|
deps: [MAT_DATE_LOCALE],
|
|
1164
1166
|
},
|
|
1165
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1167
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
1166
1168
|
], usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"offer().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"offer().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"offer().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"offer().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Offer amount</mat-label>\n <input matInput [(ngModel)]=\"offer().amount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"offer().startDate\"\n (dateInput)=\"offer().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatOptionModule }] });
|
|
1167
1169
|
}
|
|
1168
1170
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferEdit, decorators: [{
|
|
@@ -1173,7 +1175,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1173
1175
|
useClass: MomentDateAdapter,
|
|
1174
1176
|
deps: [MAT_DATE_LOCALE],
|
|
1175
1177
|
},
|
|
1176
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1178
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
1177
1179
|
], template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"offer().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"offer().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"offer().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"offer().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Offer amount</mat-label>\n <input matInput [(ngModel)]=\"offer().amount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"offer().startDate\"\n (dateInput)=\"offer().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
|
|
1178
1180
|
}], propDecorators: { offer: [{ type: i0.Input, args: [{ isSignal: true, alias: "offer", required: true }] }, { type: i0.Output, args: ["offerChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
|
|
1179
1181
|
|
|
@@ -1335,12 +1337,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1335
1337
|
|
|
1336
1338
|
class OfferRentalTermsCard {
|
|
1337
1339
|
ctx = inject(OfferDetailContext);
|
|
1340
|
+
humanize(value) {
|
|
1341
|
+
if (!value) {
|
|
1342
|
+
return '—';
|
|
1343
|
+
}
|
|
1344
|
+
return value
|
|
1345
|
+
.trim()
|
|
1346
|
+
.replace(/[_-]+/g, ' ')
|
|
1347
|
+
.toLowerCase()
|
|
1348
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1349
|
+
}
|
|
1350
|
+
boolLabel(value) {
|
|
1351
|
+
if (value === null || value === undefined) {
|
|
1352
|
+
return '—';
|
|
1353
|
+
}
|
|
1354
|
+
return value ? 'Yes' : 'No';
|
|
1355
|
+
}
|
|
1338
1356
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1339
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1357
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-rental-terms-card\">\n <div class=\"offer-rental-terms-card__head\">\n <div>\n <span class=\"offer-rental-terms-card__eyebrow\">Submitted terms</span>\n <h3 class=\"offer-rental-terms-card__title\">Original rental offer</h3>\n <p class=\"offer-rental-terms-card__description\">These were the first rental terms attached to the submission.</p>\n </div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"offer-rental-terms-card__grid\">\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Offer amount</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Move-in date</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Payment frequency</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Tenancy length</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.tenancyLengthMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Break clause</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.breakClauseMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Pets</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Smoker</span>\n <strong class=\"offer-rental-terms-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Furniture</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.furnitureRequirement) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field offer-rental-terms-card__field--wide\">\n <span class=\"offer-rental-terms-card__label\">Additional requests</span>\n <strong class=\"offer-rental-terms-card__value offer-rental-terms-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-rental-terms-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-rental-terms-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-rental-terms-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-rental-terms-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-rental-terms-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-rental-terms-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-rental-terms-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-rental-terms-card__field--wide{grid-column:1/-1}.offer-rental-terms-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-rental-terms-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-rental-terms-card__value--multiline{white-space:pre-wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
1340
1358
|
}
|
|
1341
1359
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, decorators: [{
|
|
1342
1360
|
type: Component,
|
|
1343
|
-
args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1361
|
+
args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-rental-terms-card\">\n <div class=\"offer-rental-terms-card__head\">\n <div>\n <span class=\"offer-rental-terms-card__eyebrow\">Submitted terms</span>\n <h3 class=\"offer-rental-terms-card__title\">Original rental offer</h3>\n <p class=\"offer-rental-terms-card__description\">These were the first rental terms attached to the submission.</p>\n </div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"offer-rental-terms-card__grid\">\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Offer amount</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Move-in date</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Payment frequency</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Tenancy length</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.tenancyLengthMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Break clause</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.breakClauseMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Pets</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Smoker</span>\n <strong class=\"offer-rental-terms-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Furniture</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.furnitureRequirement) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field offer-rental-terms-card__field--wide\">\n <span class=\"offer-rental-terms-card__label\">Additional requests</span>\n <strong class=\"offer-rental-terms-card__value offer-rental-terms-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-rental-terms-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-rental-terms-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-rental-terms-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-rental-terms-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-rental-terms-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-rental-terms-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-rental-terms-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-rental-terms-card__field--wide{grid-column:1/-1}.offer-rental-terms-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-rental-terms-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-rental-terms-card__value--multiline{white-space:pre-wrap}\n"] }]
|
|
1344
1362
|
}] });
|
|
1345
1363
|
|
|
1346
1364
|
class OfferTenantExpanded {
|
|
@@ -1412,11 +1430,11 @@ class OfferTenantCard {
|
|
|
1412
1430
|
this.onToggleInput()(this.index());
|
|
1413
1431
|
}
|
|
1414
1432
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1415
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantCard, isStandalone: true, selector: "rolatech-offer-tenant-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"
|
|
1433
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantCard, isStandalone: true, selector: "rolatech-offer-tenant-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"offer-tenant-card\">\n <button\n type=\"button\"\n class=\"offer-tenant-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-tenant-card__copy\">\n <div class=\"offer-tenant-card__topline\">Applicant {{ index() + 1 }}</div>\n <div class=\"offer-tenant-card__name\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"offer-tenant-card__contact\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"offer-tenant-card__chips\">\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"offer-tenant-card__arrow\" [class.offer-tenant-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-tenant-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-tenant-card__body\">\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-tenant-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-tenant-card__toggle{display:flex;width:100%;padding:1rem;gap:.9rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-tenant-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-tenant-card__copy{display:flex;flex:1;min-width:0;flex-direction:column;gap:.35rem}.offer-tenant-card__topline{color:var(--rt-brand-color);font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.offer-tenant-card__name{color:var(--rt-text-primary);font-size:1rem;font-weight:700;line-height:1.3}.offer-tenant-card__contact{color:var(--rt-text-secondary);font-size:.82rem;line-height:1.5}.offer-tenant-card__chips{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.2rem}.offer-tenant-card__chip{display:inline-flex;align-items:center;min-height:1.85rem;padding:.28rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}.offer-tenant-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-tenant-card__arrow--expanded .offer-tenant-card__icon{transform:rotate(180deg)}.offer-tenant-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-tenant-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"], dependencies: [{ kind: "component", type: OfferTenantExpanded, selector: "rolatech-offer-tenant-expanded", inputs: ["tenant"] }] });
|
|
1416
1434
|
}
|
|
1417
1435
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, decorators: [{
|
|
1418
1436
|
type: Component,
|
|
1419
|
-
args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], host: { class: 'block' }, template: "<div class=\"
|
|
1437
|
+
args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], host: { class: 'block' }, template: "<div class=\"offer-tenant-card\">\n <button\n type=\"button\"\n class=\"offer-tenant-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-tenant-card__copy\">\n <div class=\"offer-tenant-card__topline\">Applicant {{ index() + 1 }}</div>\n <div class=\"offer-tenant-card__name\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"offer-tenant-card__contact\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"offer-tenant-card__chips\">\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"offer-tenant-card__arrow\" [class.offer-tenant-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-tenant-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-tenant-card__body\">\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-tenant-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-tenant-card__toggle{display:flex;width:100%;padding:1rem;gap:.9rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-tenant-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-tenant-card__copy{display:flex;flex:1;min-width:0;flex-direction:column;gap:.35rem}.offer-tenant-card__topline{color:var(--rt-brand-color);font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.offer-tenant-card__name{color:var(--rt-text-primary);font-size:1rem;font-weight:700;line-height:1.3}.offer-tenant-card__contact{color:var(--rt-text-secondary);font-size:.82rem;line-height:1.5}.offer-tenant-card__chips{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.2rem}.offer-tenant-card__chip{display:inline-flex;align-items:center;min-height:1.85rem;padding:.28rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}.offer-tenant-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-tenant-card__arrow--expanded .offer-tenant-card__icon{transform:rotate(180deg)}.offer-tenant-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-tenant-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"] }]
|
|
1420
1438
|
}], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: true }] }], tenant: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenant", required: true }] }], onToggleInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "onToggleInput", required: true }] }] } });
|
|
1421
1439
|
|
|
1422
1440
|
class OfferTenantsAccordion {
|
|
@@ -1448,11 +1466,11 @@ class OfferTenantsAccordion {
|
|
|
1448
1466
|
this.expandedIndexes.set(new Set());
|
|
1449
1467
|
}
|
|
1450
1468
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1451
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"
|
|
1469
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"offer-tenants-accordion\">\n <div class=\"offer-tenants-accordion__head\">\n <div class=\"offer-tenants-accordion__copy\">\n <span class=\"offer-tenants-accordion__eyebrow\">Applicants</span>\n <h3 class=\"offer-tenants-accordion__title\">Tenant profiles</h3>\n <p class=\"offer-tenants-accordion__description\">\n Review identity, affordability, and guarantor information for each applicant attached to the offer.\n </p>\n </div>\n\n <div class=\"offer-tenants-accordion__tools\">\n <span class=\"offer-tenants-accordion__count\">{{ ts.length }} applicants</span>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-tenants-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-tenants-accordion__empty\">No applicants have been attached to this offer yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-tenants-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-tenants-accordion__head,.offer-tenants-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-tenants-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-tenants-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-tenants-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-tenants-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-tenants-accordion__tool,.offer-tenants-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-tenants-accordion__tool{cursor:pointer;transition:border-color .2s ease,background .2s ease,color .2s ease}.offer-tenants-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-tenants-accordion__tool:disabled{cursor:not-allowed;opacity:.55}.offer-tenants-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-tenants-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-tenants-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"], dependencies: [{ kind: "component", type: OfferTenantCard, selector: "rolatech-offer-tenant-card", inputs: ["index", "expanded", "tenant", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1452
1470
|
}
|
|
1453
1471
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, decorators: [{
|
|
1454
1472
|
type: Component,
|
|
1455
|
-
args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (tenants(); as ts) {\n<section class=\"
|
|
1473
|
+
args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (tenants(); as ts) {\n<section class=\"offer-tenants-accordion\">\n <div class=\"offer-tenants-accordion__head\">\n <div class=\"offer-tenants-accordion__copy\">\n <span class=\"offer-tenants-accordion__eyebrow\">Applicants</span>\n <h3 class=\"offer-tenants-accordion__title\">Tenant profiles</h3>\n <p class=\"offer-tenants-accordion__description\">\n Review identity, affordability, and guarantor information for each applicant attached to the offer.\n </p>\n </div>\n\n <div class=\"offer-tenants-accordion__tools\">\n <span class=\"offer-tenants-accordion__count\">{{ ts.length }} applicants</span>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-tenants-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-tenants-accordion__empty\">No applicants have been attached to this offer yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-tenants-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-tenants-accordion__head,.offer-tenants-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-tenants-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-tenants-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-tenants-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-tenants-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-tenants-accordion__tool,.offer-tenants-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-tenants-accordion__tool{cursor:pointer;transition:border-color .2s ease,background .2s ease,color .2s ease}.offer-tenants-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-tenants-accordion__tool:disabled{cursor:not-allowed;opacity:.55}.offer-tenants-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-tenants-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-tenants-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"] }]
|
|
1456
1474
|
}] });
|
|
1457
1475
|
|
|
1458
1476
|
class OfferItemCard {
|
|
@@ -1462,21 +1480,31 @@ class OfferItemCard {
|
|
|
1462
1480
|
return m?.url ?? null;
|
|
1463
1481
|
}
|
|
1464
1482
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1465
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1483
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-item-card\">\n <div class=\"offer-item-card__head\">\n <div class=\"offer-item-card__copy\">\n <span class=\"offer-item-card__eyebrow\">Property snapshot</span>\n <h3 class=\"offer-item-card__title\">{{ o.item.title }}</h3>\n </div>\n <span class=\"offer-item-card__property-chip\">Property ID {{ o.item.propertyId }}</span>\n </div>\n\n <div class=\"offer-item-card__content\">\n @if (firstMediaUrl(o); as mediaUrl) {\n <img class=\"offer-item-card__media\" [src]=\"mediaUrl\" [alt]=\"o.item.title\" />\n } @else {\n <div class=\"offer-item-card__placeholder\">Property image unavailable</div>\n }\n\n <div class=\"offer-item-card__panel\">\n <div class=\"offer-item-card__price\">{{ o.item.amount | price }}</div>\n\n <div class=\"offer-item-card__facts\">\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bedrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bedrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bathrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bathrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Receptions</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.receptions }}</strong>\n </article>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-item-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:var(--rt-base-background, #ffffff);box-shadow:0 18px 38px -34px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.offer-item-card__head,.offer-item-card__copy{display:flex;flex-direction:column;gap:.45rem}.offer-item-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.3rem .64rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-item-card__title{margin:0;color:var(--rt-text-primary);font-size:1.2rem;line-height:1.2;font-weight:750}.offer-item-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-item-card__property-chip{display:inline-flex;align-self:flex-start;min-height:2rem;padding:.38rem .7rem;border-radius:999px;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-item-card__content{display:grid;gap:1rem}.offer-item-card__media,.offer-item-card__placeholder{width:100%;min-height:15rem;border-radius:1.15rem}.offer-item-card__media{object-fit:cover;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent)}.offer-item-card__placeholder{display:grid;place-items:center;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 86%,var(--rt-brand-color) 14%));color:var(--rt-text-secondary);text-align:center;padding:1rem}.offer-item-card__panel{display:flex;flex-direction:column;gap:1rem}.offer-item-card__price{color:var(--rt-text-primary);font-size:clamp(1.5rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-item-card__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.85rem}.offer-item-card__fact{display:flex;flex-direction:column;gap:.2rem;padding:.85rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-item-card__fact-label{color:var(--rt-text-secondary);font-size:.76rem}.offer-item-card__fact-value{color:var(--rt-text-primary);font-size:1rem}@media(min-width:960px){.offer-item-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-item-card__content{grid-template-columns:minmax(0,1.1fr) minmax(18rem,.95fr);align-items:start}.offer-item-card__media{min-height:18rem}}\n"], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
1466
1484
|
}
|
|
1467
1485
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, decorators: [{
|
|
1468
1486
|
type: Component,
|
|
1469
|
-
args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1487
|
+
args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-item-card\">\n <div class=\"offer-item-card__head\">\n <div class=\"offer-item-card__copy\">\n <span class=\"offer-item-card__eyebrow\">Property snapshot</span>\n <h3 class=\"offer-item-card__title\">{{ o.item.title }}</h3>\n </div>\n <span class=\"offer-item-card__property-chip\">Property ID {{ o.item.propertyId }}</span>\n </div>\n\n <div class=\"offer-item-card__content\">\n @if (firstMediaUrl(o); as mediaUrl) {\n <img class=\"offer-item-card__media\" [src]=\"mediaUrl\" [alt]=\"o.item.title\" />\n } @else {\n <div class=\"offer-item-card__placeholder\">Property image unavailable</div>\n }\n\n <div class=\"offer-item-card__panel\">\n <div class=\"offer-item-card__price\">{{ o.item.amount | price }}</div>\n\n <div class=\"offer-item-card__facts\">\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bedrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bedrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bathrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bathrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Receptions</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.receptions }}</strong>\n </article>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-item-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:var(--rt-base-background, #ffffff);box-shadow:0 18px 38px -34px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.offer-item-card__head,.offer-item-card__copy{display:flex;flex-direction:column;gap:.45rem}.offer-item-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.3rem .64rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-item-card__title{margin:0;color:var(--rt-text-primary);font-size:1.2rem;line-height:1.2;font-weight:750}.offer-item-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-item-card__property-chip{display:inline-flex;align-self:flex-start;min-height:2rem;padding:.38rem .7rem;border-radius:999px;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-item-card__content{display:grid;gap:1rem}.offer-item-card__media,.offer-item-card__placeholder{width:100%;min-height:15rem;border-radius:1.15rem}.offer-item-card__media{object-fit:cover;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent)}.offer-item-card__placeholder{display:grid;place-items:center;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 86%,var(--rt-brand-color) 14%));color:var(--rt-text-secondary);text-align:center;padding:1rem}.offer-item-card__panel{display:flex;flex-direction:column;gap:1rem}.offer-item-card__price{color:var(--rt-text-primary);font-size:clamp(1.5rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-item-card__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.85rem}.offer-item-card__fact{display:flex;flex-direction:column;gap:.2rem;padding:.85rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-item-card__fact-label{color:var(--rt-text-secondary);font-size:.76rem}.offer-item-card__fact-value{color:var(--rt-text-primary);font-size:1rem}@media(min-width:960px){.offer-item-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-item-card__content{grid-template-columns:minmax(0,1.1fr) minmax(18rem,.95fr);align-items:start}.offer-item-card__media{min-height:18rem}}\n"] }]
|
|
1470
1488
|
}] });
|
|
1471
1489
|
|
|
1472
1490
|
class OfferSaleDetailsCard {
|
|
1473
1491
|
ctx = inject(OfferDetailContext);
|
|
1492
|
+
humanize(value) {
|
|
1493
|
+
if (!value) {
|
|
1494
|
+
return '—';
|
|
1495
|
+
}
|
|
1496
|
+
return value
|
|
1497
|
+
.trim()
|
|
1498
|
+
.replace(/[_-]+/g, ' ')
|
|
1499
|
+
.toLowerCase()
|
|
1500
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1501
|
+
}
|
|
1474
1502
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1475
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1503
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-sale-details-card\">\n <div class=\"offer-sale-details-card__head\">\n <div>\n <span class=\"offer-sale-details-card__eyebrow\">Buyer terms</span>\n <h3 class=\"offer-sale-details-card__title\">Sale offer details</h3>\n <p class=\"offer-sale-details-card__description\">Buyer details and funding information attached to the submitted sale offer.</p>\n </div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"offer-sale-details-card__grid\">\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Offer amount</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.amount | price }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Payment method</span>\n <strong class=\"offer-sale-details-card__value\">{{ humanize(s.paymentMethod) }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Buyer name</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.buyerName ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Phone</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.phone ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Email</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.email ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Exchange target</span>\n <strong class=\"offer-sale-details-card__value\">{{\n s.proposedExchangeDate ? (s.proposedExchangeDate | date: 'mediumDate') : '\u2014'\n }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field offer-sale-details-card__field--wide\">\n <span class=\"offer-sale-details-card__label\">Solicitor company</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.solicitorCompanyName ?? '\u2014' }}</strong>\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-sale-details-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-sale-details-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-sale-details-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-sale-details-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-sale-details-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-sale-details-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-sale-details-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-sale-details-card__field--wide{grid-column:1/-1}.offer-sale-details-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-sale-details-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
1476
1504
|
}
|
|
1477
1505
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, decorators: [{
|
|
1478
1506
|
type: Component,
|
|
1479
|
-
args: [{ selector: 'rolatech-offer-sale-details-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1507
|
+
args: [{ selector: 'rolatech-offer-sale-details-card', imports: [CommonModule, PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-sale-details-card\">\n <div class=\"offer-sale-details-card__head\">\n <div>\n <span class=\"offer-sale-details-card__eyebrow\">Buyer terms</span>\n <h3 class=\"offer-sale-details-card__title\">Sale offer details</h3>\n <p class=\"offer-sale-details-card__description\">Buyer details and funding information attached to the submitted sale offer.</p>\n </div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"offer-sale-details-card__grid\">\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Offer amount</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.amount | price }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Payment method</span>\n <strong class=\"offer-sale-details-card__value\">{{ humanize(s.paymentMethod) }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Buyer name</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.buyerName ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Phone</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.phone ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Email</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.email ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Exchange target</span>\n <strong class=\"offer-sale-details-card__value\">{{\n s.proposedExchangeDate ? (s.proposedExchangeDate | date: 'mediumDate') : '\u2014'\n }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field offer-sale-details-card__field--wide\">\n <span class=\"offer-sale-details-card__label\">Solicitor company</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.solicitorCompanyName ?? '\u2014' }}</strong>\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-sale-details-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-sale-details-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-sale-details-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-sale-details-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-sale-details-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-sale-details-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-sale-details-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-sale-details-card__field--wide{grid-column:1/-1}.offer-sale-details-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-sale-details-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}\n"] }]
|
|
1480
1508
|
}] });
|
|
1481
1509
|
|
|
1482
1510
|
// offer-manage.facade.ts
|
|
@@ -1878,11 +1906,11 @@ class OfferHistoryCard {
|
|
|
1878
1906
|
return `${this.actorLabel(h.actor)} • ${this.actionLabel(h.action)}`;
|
|
1879
1907
|
}
|
|
1880
1908
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1881
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryCard, isStandalone: true, selector: "rolatech-offer-history-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, history: { classPropertyName: "history", publicName: "history", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"
|
|
1909
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryCard, isStandalone: true, selector: "rolatech-offer-history-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, history: { classPropertyName: "history", publicName: "history", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"offer-history-card\">\n <button\n type=\"button\"\n class=\"offer-history-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-history-card__copy\">\n <div class=\"offer-history-card__meta\">\n <span class=\"offer-history-card__actor\">{{ actorLabel(history().actor) }}</span>\n <span class=\"offer-history-card__timestamp\">{{ formatTs(history().timestamp) }}</span>\n </div>\n <div class=\"offer-history-card__summary\">{{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"offer-history-card__arrow\" [class.offer-history-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-history-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-history-card__body\">\n <rolatech-offer-history-expanded [history]=\"history()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-history-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-history-card__toggle{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.9rem;padding:1rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-history-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-history-card__copy{display:flex;min-width:0;flex:1;flex-direction:column;gap:.35rem}.offer-history-card__meta{display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}.offer-history-card__actor{display:inline-flex;align-items:center;min-height:1.8rem;padding:.26rem .58rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700}.offer-history-card__timestamp{color:var(--rt-text-secondary);font-size:.8rem}.offer-history-card__summary{color:var(--rt-text-primary);font-size:.98rem;font-weight:700;line-height:1.35}.offer-history-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-history-card__arrow--expanded .offer-history-card__icon{transform:rotate(180deg)}.offer-history-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-history-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferHistoryExpanded, selector: "rolatech-offer-history-expanded", inputs: ["history"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1882
1910
|
}
|
|
1883
1911
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, decorators: [{
|
|
1884
1912
|
type: Component,
|
|
1885
|
-
args: [{ selector: 'rolatech-offer-history-card', imports: [CommonModule, OfferHistoryExpanded], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "<div class=\"
|
|
1913
|
+
args: [{ selector: 'rolatech-offer-history-card', imports: [CommonModule, OfferHistoryExpanded], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "<div class=\"offer-history-card\">\n <button\n type=\"button\"\n class=\"offer-history-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-history-card__copy\">\n <div class=\"offer-history-card__meta\">\n <span class=\"offer-history-card__actor\">{{ actorLabel(history().actor) }}</span>\n <span class=\"offer-history-card__timestamp\">{{ formatTs(history().timestamp) }}</span>\n </div>\n <div class=\"offer-history-card__summary\">{{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"offer-history-card__arrow\" [class.offer-history-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-history-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-history-card__body\">\n <rolatech-offer-history-expanded [history]=\"history()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-history-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-history-card__toggle{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.9rem;padding:1rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-history-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-history-card__copy{display:flex;min-width:0;flex:1;flex-direction:column;gap:.35rem}.offer-history-card__meta{display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}.offer-history-card__actor{display:inline-flex;align-items:center;min-height:1.8rem;padding:.26rem .58rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700}.offer-history-card__timestamp{color:var(--rt-text-secondary);font-size:.8rem}.offer-history-card__summary{color:var(--rt-text-primary);font-size:.98rem;font-weight:700;line-height:1.35}.offer-history-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-history-card__arrow--expanded .offer-history-card__icon{transform:rotate(180deg)}.offer-history-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-history-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"] }]
|
|
1886
1914
|
}], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: true }] }], history: [{ type: i0.Input, args: [{ isSignal: true, alias: "history", required: true }] }], onToggleInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "onToggleInput", required: true }] }] } });
|
|
1887
1915
|
|
|
1888
1916
|
class OfferHistoryAccordion {
|
|
@@ -1926,14 +1954,14 @@ class OfferHistoryAccordion {
|
|
|
1926
1954
|
}
|
|
1927
1955
|
}
|
|
1928
1956
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1929
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryAccordion, isStandalone: true, selector: "rolatech-offer-history-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (history(); as ts) {\n<section class=\"
|
|
1957
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryAccordion, isStandalone: true, selector: "rolatech-offer-history-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (history(); as ts) {\n<section class=\"offer-history-accordion\">\n <div class=\"offer-history-accordion__head\">\n <div class=\"offer-history-accordion__copy\">\n <span class=\"offer-history-accordion__eyebrow\">Timeline</span>\n <h3 class=\"offer-history-accordion__title\">Offer history</h3>\n <p class=\"offer-history-accordion__description\">\n Every submitted change, view, and response is recorded here so you can audit the full negotiation trail.\n </p>\n </div>\n\n <div class=\"offer-history-accordion__tools\">\n <span class=\"offer-history-accordion__count\">{{ ts.length }} events</span>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-history-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-history-accordion__empty\">No recorded offer events yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-history-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-history-accordion__head,.offer-history-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-history-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-history-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-history-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-history-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-history-accordion__tool,.offer-history-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-history-accordion__tool{cursor:pointer}.offer-history-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-history-accordion__tool:disabled{opacity:.55;cursor:not-allowed}.offer-history-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-history-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-history-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"], dependencies: [{ kind: "component", type: OfferHistoryCard, selector: "rolatech-offer-history-card", inputs: ["index", "expanded", "history", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1930
1958
|
}
|
|
1931
1959
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, decorators: [{
|
|
1932
1960
|
type: Component,
|
|
1933
|
-
args: [{ selector: 'rolatech-offer-history-accordion', imports: [OfferHistoryCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (history(); as ts) {\n<section class=\"
|
|
1961
|
+
args: [{ selector: 'rolatech-offer-history-accordion', imports: [OfferHistoryCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (history(); as ts) {\n<section class=\"offer-history-accordion\">\n <div class=\"offer-history-accordion__head\">\n <div class=\"offer-history-accordion__copy\">\n <span class=\"offer-history-accordion__eyebrow\">Timeline</span>\n <h3 class=\"offer-history-accordion__title\">Offer history</h3>\n <p class=\"offer-history-accordion__description\">\n Every submitted change, view, and response is recorded here so you can audit the full negotiation trail.\n </p>\n </div>\n\n <div class=\"offer-history-accordion__tools\">\n <span class=\"offer-history-accordion__count\">{{ ts.length }} events</span>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-history-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-history-accordion__empty\">No recorded offer events yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-history-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-history-accordion__head,.offer-history-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-history-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-history-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-history-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-history-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-history-accordion__tool,.offer-history-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-history-accordion__tool{cursor:pointer}.offer-history-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-history-accordion__tool:disabled{opacity:.55;cursor:not-allowed}.offer-history-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-history-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-history-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"] }]
|
|
1934
1962
|
}] });
|
|
1935
1963
|
|
|
1936
|
-
const MY_FORMATS$
|
|
1964
|
+
const MY_FORMATS$6 = {
|
|
1937
1965
|
parse: {
|
|
1938
1966
|
dateInput: 'YYYY-MM-DD',
|
|
1939
1967
|
},
|
|
@@ -2015,7 +2043,7 @@ class OfferCounterDialog {
|
|
|
2015
2043
|
useClass: MomentDateAdapter,
|
|
2016
2044
|
deps: [MAT_DATE_LOCALE],
|
|
2017
2045
|
},
|
|
2018
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
2046
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
|
|
2019
2047
|
], ngImport: i0, template: "<div class=\"grid\">\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Offer amount (PCM)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().amount\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Move-in date</mat-label>\n <input matInput [min]=\"minDate\" (focus)=\"dp.open()\" [matDatepicker]=\"dp\" [(ngModel)]=\"term().moveInDate\" readonly />\n <mat-datepicker-toggle matIconSuffix [for]=\"dp\"></mat-datepicker-toggle>\n <mat-datepicker #dp></mat-datepicker>\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Payment frequency</mat-label>\n <mat-select [(ngModel)]=\"term().paymentFrequency\">\n <mat-option value=\"MONTHLY\">Monthly</mat-option>\n <mat-option value=\"QUARTERLY\">Quarterly</mat-option>\n <mat-option value=\"SEMI_ANNUALLY\">Semi-annually</mat-option>\n <mat-option value=\"ANNUALLY\">Annually</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Tenancy length (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().tenancyLengthMonths\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Break clause (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select [(ngModel)]=\"term().furniture\">\n <mat-option value=\"FURNISHED\">Furnished</mat-option>\n <mat-option value=\"UNFURNISHED\">Unfurnished</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\" class=\"w-full\">\n <mat-label>{{ petsInputFocused() ? 'Pets' : 'Add a pet (e.g., Dog, Cat)' }}</mat-label>\n\n <mat-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of (term().pets); track p) {\n <mat-chip-row (removed)=\"removePet(p)\">\n {{ p }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + p\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n\n <input\n matInput\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (focus)=\"petsInputFocused.set(true)\"\n (blur)=\"petsInputFocused.set(false)\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n @if ((term().pets).length <= 0) {\n <mat-error>At least one pet or none is required</mat-error>\n }\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" [(ngModel)]=\"term().smoker\">\n <span class=\"text-lg font-bold\">Smoker</span>\n </mat-slide-toggle>\n\n <mat-form-field class=\"span2\">\n <mat-label>Additional requests</mat-label>\n <textarea matInput rows=\"3\" [(ngModel)]=\"term().additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i5$1.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i7.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i7.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i7.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i7.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
2020
2048
|
}
|
|
2021
2049
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterDialog, decorators: [{
|
|
@@ -2037,7 +2065,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
2037
2065
|
useClass: MomentDateAdapter,
|
|
2038
2066
|
deps: [MAT_DATE_LOCALE],
|
|
2039
2067
|
},
|
|
2040
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
2068
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
|
|
2041
2069
|
], template: "<div class=\"grid\">\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Offer amount (PCM)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().amount\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Move-in date</mat-label>\n <input matInput [min]=\"minDate\" (focus)=\"dp.open()\" [matDatepicker]=\"dp\" [(ngModel)]=\"term().moveInDate\" readonly />\n <mat-datepicker-toggle matIconSuffix [for]=\"dp\"></mat-datepicker-toggle>\n <mat-datepicker #dp></mat-datepicker>\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Payment frequency</mat-label>\n <mat-select [(ngModel)]=\"term().paymentFrequency\">\n <mat-option value=\"MONTHLY\">Monthly</mat-option>\n <mat-option value=\"QUARTERLY\">Quarterly</mat-option>\n <mat-option value=\"SEMI_ANNUALLY\">Semi-annually</mat-option>\n <mat-option value=\"ANNUALLY\">Annually</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Tenancy length (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().tenancyLengthMonths\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Break clause (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select [(ngModel)]=\"term().furniture\">\n <mat-option value=\"FURNISHED\">Furnished</mat-option>\n <mat-option value=\"UNFURNISHED\">Unfurnished</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\" class=\"w-full\">\n <mat-label>{{ petsInputFocused() ? 'Pets' : 'Add a pet (e.g., Dog, Cat)' }}</mat-label>\n\n <mat-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of (term().pets); track p) {\n <mat-chip-row (removed)=\"removePet(p)\">\n {{ p }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + p\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n\n <input\n matInput\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (focus)=\"petsInputFocused.set(true)\"\n (blur)=\"petsInputFocused.set(false)\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n @if ((term().pets).length <= 0) {\n <mat-error>At least one pet or none is required</mat-error>\n }\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" [(ngModel)]=\"term().smoker\">\n <span class=\"text-lg font-bold\">Smoker</span>\n </mat-slide-toggle>\n\n <mat-form-field class=\"span2\">\n <mat-label>Additional requests</mat-label>\n <textarea matInput rows=\"3\" [(ngModel)]=\"term().additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n" }]
|
|
2042
2070
|
}], propDecorators: { term: [{ type: i0.Input, args: [{ isSignal: true, alias: "term", required: true }] }, { type: i0.Output, args: ["termChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
|
|
2043
2071
|
|
|
@@ -2860,22 +2888,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
2860
2888
|
|
|
2861
2889
|
class OfferCounterLatestCard {
|
|
2862
2890
|
f = inject(PropertyOfferNegotiationFacade);
|
|
2891
|
+
humanize(value) {
|
|
2892
|
+
if (!value) {
|
|
2893
|
+
return '—';
|
|
2894
|
+
}
|
|
2895
|
+
return value
|
|
2896
|
+
.trim()
|
|
2897
|
+
.replace(/[_-]+/g, ' ')
|
|
2898
|
+
.toLowerCase()
|
|
2899
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
2900
|
+
}
|
|
2901
|
+
boolLabel(value) {
|
|
2902
|
+
if (value === null || value === undefined) {
|
|
2903
|
+
return '—';
|
|
2904
|
+
}
|
|
2905
|
+
return value ? 'Yes' : 'No';
|
|
2906
|
+
}
|
|
2863
2907
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2864
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterLatestCard, isStandalone: true, selector: "rolatech-offer-counter-latest-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.latest(); as rt) {\n<section class=\"
|
|
2908
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterLatestCard, isStandalone: true, selector: "rolatech-offer-counter-latest-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.latest(); as rt) {\n<section class=\"offer-counter-latest-card\">\n <div class=\"offer-counter-latest-card__head\">\n <div>\n <span class=\"offer-counter-latest-card__eyebrow\">Negotiated terms</span>\n <h3 class=\"offer-counter-latest-card__title\">Latest offer version</h3>\n <p class=\"offer-counter-latest-card__description\">This version reflects the newest negotiated rental terms.</p>\n </div>\n\n <div class=\"offer-counter-latest-card__badges\">\n @if (f.editedFields().size) {\n <span class=\"offer-counter-latest-card__badge\">{{ f.editedFields().size }} updated fields</span>\n } @if (f.editMode()) {\n <span class=\"offer-counter-latest-card__badge offer-counter-latest-card__badge--active\">Editing</span>\n }\n </div>\n </div>\n\n @if (f.editMode()) {\n <div class=\"offer-counter-latest-card__editor\">\n <rolatech-offer-counter-edit />\n </div>\n } @else {\n <div class=\"offer-counter-latest-card__grid\">\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Offer amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field offer-counter-latest-card__field--wide\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value offer-counter-latest-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-counter-latest-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 10%,var(--rt-base-background, #ffffff)),color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent))}.offer-counter-latest-card__head{display:flex;flex-direction:column;gap:.75rem}.offer-counter-latest-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:#3b82f61f;color:#1d4ed8;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-latest-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:760}.offer-counter-latest-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-latest-card__badges{display:flex;flex-wrap:wrap;gap:.5rem}.offer-counter-latest-card__badge,.offer-counter-latest-card__edited{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border-radius:999px;font-size:.76rem;font-weight:700}.offer-counter-latest-card__badge{border:1px solid rgba(59,130,246,.18);background:#3b82f61a;color:#1d4ed8}.offer-counter-latest-card__badge--active{border-color:#10b98133;background:#10b9811f;color:#047857}.offer-counter-latest-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-latest-card__field{display:flex;flex-direction:column;gap:.3rem;padding:.92rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.offer-counter-latest-card__field--wide{grid-column:1/-1}.offer-counter-latest-card__label-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.offer-counter-latest-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-latest-card__edited{border:1px solid rgba(249,115,22,.22);background:#f973161a;color:#c2410c}.offer-counter-latest-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-counter-latest-card__value--multiline{white-space:pre-wrap}.offer-counter-latest-card__editor{padding:.25rem 0 0}@media(min-width:960px){.offer-counter-latest-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferCounterEdit, selector: "rolatech-offer-counter-edit" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
2865
2909
|
}
|
|
2866
2910
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, decorators: [{
|
|
2867
2911
|
type: Component,
|
|
2868
|
-
args: [{ selector: 'rolatech-offer-counter-latest-card', imports: [CommonModule, PricePipe, OfferCounterEdit, MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.latest(); as rt) {\n<section class=\"
|
|
2912
|
+
args: [{ selector: 'rolatech-offer-counter-latest-card', imports: [CommonModule, PricePipe, OfferCounterEdit, MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.latest(); as rt) {\n<section class=\"offer-counter-latest-card\">\n <div class=\"offer-counter-latest-card__head\">\n <div>\n <span class=\"offer-counter-latest-card__eyebrow\">Negotiated terms</span>\n <h3 class=\"offer-counter-latest-card__title\">Latest offer version</h3>\n <p class=\"offer-counter-latest-card__description\">This version reflects the newest negotiated rental terms.</p>\n </div>\n\n <div class=\"offer-counter-latest-card__badges\">\n @if (f.editedFields().size) {\n <span class=\"offer-counter-latest-card__badge\">{{ f.editedFields().size }} updated fields</span>\n } @if (f.editMode()) {\n <span class=\"offer-counter-latest-card__badge offer-counter-latest-card__badge--active\">Editing</span>\n }\n </div>\n </div>\n\n @if (f.editMode()) {\n <div class=\"offer-counter-latest-card__editor\">\n <rolatech-offer-counter-edit />\n </div>\n } @else {\n <div class=\"offer-counter-latest-card__grid\">\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Offer amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field offer-counter-latest-card__field--wide\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value offer-counter-latest-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-counter-latest-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 10%,var(--rt-base-background, #ffffff)),color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent))}.offer-counter-latest-card__head{display:flex;flex-direction:column;gap:.75rem}.offer-counter-latest-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:#3b82f61f;color:#1d4ed8;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-latest-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:760}.offer-counter-latest-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-latest-card__badges{display:flex;flex-wrap:wrap;gap:.5rem}.offer-counter-latest-card__badge,.offer-counter-latest-card__edited{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border-radius:999px;font-size:.76rem;font-weight:700}.offer-counter-latest-card__badge{border:1px solid rgba(59,130,246,.18);background:#3b82f61a;color:#1d4ed8}.offer-counter-latest-card__badge--active{border-color:#10b98133;background:#10b9811f;color:#047857}.offer-counter-latest-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-latest-card__field{display:flex;flex-direction:column;gap:.3rem;padding:.92rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.offer-counter-latest-card__field--wide{grid-column:1/-1}.offer-counter-latest-card__label-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.offer-counter-latest-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-latest-card__edited{border:1px solid rgba(249,115,22,.22);background:#f973161a;color:#c2410c}.offer-counter-latest-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-counter-latest-card__value--multiline{white-space:pre-wrap}.offer-counter-latest-card__editor{padding:.25rem 0 0}@media(min-width:960px){.offer-counter-latest-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between}}\n"] }]
|
|
2869
2913
|
}] });
|
|
2870
2914
|
|
|
2871
2915
|
class OfferCounterPreviousCard {
|
|
2872
2916
|
f = inject(PropertyOfferNegotiationFacade);
|
|
2917
|
+
humanize(value) {
|
|
2918
|
+
if (!value) {
|
|
2919
|
+
return '—';
|
|
2920
|
+
}
|
|
2921
|
+
return value
|
|
2922
|
+
.trim()
|
|
2923
|
+
.replace(/[_-]+/g, ' ')
|
|
2924
|
+
.toLowerCase()
|
|
2925
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
2926
|
+
}
|
|
2927
|
+
boolLabel(value) {
|
|
2928
|
+
if (value === null || value === undefined) {
|
|
2929
|
+
return '—';
|
|
2930
|
+
}
|
|
2931
|
+
return value ? 'Yes' : 'No';
|
|
2932
|
+
}
|
|
2873
2933
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2874
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterPreviousCard, isStandalone: true, selector: "rolatech-offer-counter-previous-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.previous(); as rt) {\n<section class=\"
|
|
2934
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterPreviousCard, isStandalone: true, selector: "rolatech-offer-counter-previous-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.previous(); as rt) {\n<section class=\"offer-counter-previous-card\">\n <div class=\"offer-counter-previous-card__head\">\n <span class=\"offer-counter-previous-card__eyebrow\">Previous version</span>\n <h3 class=\"offer-counter-previous-card__title\">Earlier rental terms</h3>\n <p class=\"offer-counter-previous-card__description\">\n Use this muted snapshot to compare the previous position against the latest counter.\n </p>\n </div>\n\n <div class=\"offer-counter-previous-card__grid\">\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Offer amount</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Move-in date</span>\n <strong class=\"offer-counter-previous-card__value\"\n >{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong\n >\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Payment frequency</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Tenancy length</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Break clause</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Pets</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Smoker</span>\n <strong class=\"offer-counter-previous-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Furniture</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field offer-counter-previous-card__field--wide\">\n <span class=\"offer-counter-previous-card__label\">Additional requests</span>\n <strong class=\"offer-counter-previous-card__value offer-counter-previous-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-counter-previous-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent);opacity:.82}.offer-counter-previous-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-counter-previous-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-text-secondary) 10%,transparent);color:var(--rt-text-secondary);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-previous-card__title{margin:0;color:var(--rt-text-primary);font-size:1.02rem;font-weight:740}.offer-counter-previous-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-previous-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-previous-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.88rem .94rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,transparent)}.offer-counter-previous-card__field--wide{grid-column:1/-1}.offer-counter-previous-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-previous-card__value{color:var(--rt-text-primary);font-size:.94rem;line-height:1.45}.offer-counter-previous-card__value--multiline{white-space:pre-wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
2875
2935
|
}
|
|
2876
2936
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, decorators: [{
|
|
2877
2937
|
type: Component,
|
|
2878
|
-
args: [{ selector: 'rolatech-offer-counter-previous-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.previous(); as rt) {\n<section class=\"
|
|
2938
|
+
args: [{ selector: 'rolatech-offer-counter-previous-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.previous(); as rt) {\n<section class=\"offer-counter-previous-card\">\n <div class=\"offer-counter-previous-card__head\">\n <span class=\"offer-counter-previous-card__eyebrow\">Previous version</span>\n <h3 class=\"offer-counter-previous-card__title\">Earlier rental terms</h3>\n <p class=\"offer-counter-previous-card__description\">\n Use this muted snapshot to compare the previous position against the latest counter.\n </p>\n </div>\n\n <div class=\"offer-counter-previous-card__grid\">\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Offer amount</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Move-in date</span>\n <strong class=\"offer-counter-previous-card__value\"\n >{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong\n >\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Payment frequency</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Tenancy length</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Break clause</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Pets</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Smoker</span>\n <strong class=\"offer-counter-previous-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Furniture</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field offer-counter-previous-card__field--wide\">\n <span class=\"offer-counter-previous-card__label\">Additional requests</span>\n <strong class=\"offer-counter-previous-card__value offer-counter-previous-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-counter-previous-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent);opacity:.82}.offer-counter-previous-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-counter-previous-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-text-secondary) 10%,transparent);color:var(--rt-text-secondary);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-previous-card__title{margin:0;color:var(--rt-text-primary);font-size:1.02rem;font-weight:740}.offer-counter-previous-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-previous-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-previous-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.88rem .94rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,transparent)}.offer-counter-previous-card__field--wide{grid-column:1/-1}.offer-counter-previous-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-previous-card__value{color:var(--rt-text-primary);font-size:.94rem;line-height:1.45}.offer-counter-previous-card__value--multiline{white-space:pre-wrap}\n"] }]
|
|
2879
2939
|
}] });
|
|
2880
2940
|
|
|
2881
2941
|
class OfferNegotiationSection {
|
|
@@ -2911,11 +2971,11 @@ class OfferNegotiationSection {
|
|
|
2911
2971
|
});
|
|
2912
2972
|
}
|
|
2913
2973
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2914
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferNegotiationSection, isStandalone: true, selector: "rolatech-offer-negotiation-section", host: { classAttribute: "block" }, ngImport: i0, template: "@if (!isTerminal()) {\n<section class=\"
|
|
2974
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferNegotiationSection, isStandalone: true, selector: "rolatech-offer-negotiation-section", host: { classAttribute: "block" }, ngImport: i0, template: "@if (!isTerminal()) {\n<section class=\"offer-negotiation-section\">\n <header class=\"offer-negotiation-section__header\">\n <div class=\"offer-negotiation-section__copy\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation controls</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">Respond to the latest offer</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n Keep the negotiation moving with one clear next action. Every response stays recorded in the offer history below.\n </p>\n </div>\n\n @if (f.lock(); as lk) {\n @if (lk.locked) {\n <div class=\"offer-negotiation-section__lock\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n }\n }\n </header>\n\n @if (f.error(); as err) {\n <div class=\"offer-negotiation-section__alert\">{{ err }}</div>\n }\n\n @if (f.editMode()) {\n <div class=\"offer-negotiation-section__edit-note\">\n Counter editing is active below. Review the updated terms, then send the counter-offer when ready.\n </div>\n }\n\n <div class=\"offer-negotiation-section__actions\">\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) {\n Working...\n } @else {\n {{ f.withdrawLabel() }}\n }\n </button>\n }\n\n <span class=\"offer-negotiation-section__spacer\"></span>\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) {\n Working...\n } @else {\n Cancel\n }\n </button>\n </div>\n\n @if (f.showLifecycleActions()) {\n <div class=\"offer-negotiation-section__secondary\">\n <div class=\"offer-negotiation-section__secondary-copy\">\n <span class=\"offer-negotiation-section__secondary-eyebrow\">Workflow steps</span>\n <h4 class=\"offer-negotiation-section__secondary-title\">Progress the deal after negotiation</h4>\n </div>\n\n <div class=\"offer-negotiation-section__secondary-actions\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n} @else {\n <section class=\"offer-negotiation-section offer-negotiation-section--terminal\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation closed</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">This offer is no longer awaiting action</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n The final state has been recorded. Use the history section below to review how the negotiation reached this outcome.\n </p>\n </section>\n}\n", styles: [":host{display:block}.offer-negotiation-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%);box-shadow:0 18px 42px -38px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.offer-negotiation-section__header,.offer-negotiation-section__copy,.offer-negotiation-section__secondary-copy{display:flex;flex-direction:column;gap:.45rem}.offer-negotiation-section__eyebrow,.offer-negotiation-section__secondary-eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .68rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-negotiation-section__heading{display:flex;flex-direction:column;gap:.75rem}.offer-negotiation-section__title,.offer-negotiation-section__secondary-title{margin:0;color:var(--rt-text-primary);font-size:1.08rem;line-height:1.2;font-weight:700}.offer-negotiation-section__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-negotiation-section__status,.offer-negotiation-section__lock{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.1rem;padding:.38rem .75rem;border-radius:999px;font-size:.8rem;font-weight:700}.offer-negotiation-section__status{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-raised-background, #ffffff);color:var(--rt-text-primary)}.offer-negotiation-section__lock{border:1px solid rgba(245,158,11,.24);background:#f59e0b1f;color:#a16207}.offer-negotiation-section__alert,.offer-negotiation-section__edit-note{padding:.85rem .95rem;border-radius:1rem;font-size:.9rem;line-height:1.55}.offer-negotiation-section__alert{border:1px solid rgba(239,68,68,.2);background:#fef2f2e6;color:#b91c1c}.offer-negotiation-section__edit-note{border:1px solid rgba(59,130,246,.16);background:#eff6ffe6;color:#1d4ed8}.offer-negotiation-section__actions,.offer-negotiation-section__secondary-actions{display:flex;flex-wrap:wrap;gap:.7rem}.offer-negotiation-section__spacer{flex:1 1 auto}.offer-negotiation-section__secondary{display:flex;flex-direction:column;gap:.85rem;padding-top:1rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-negotiation-section--terminal{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent)}@media(min-width:900px){.offer-negotiation-section__header{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-negotiation-section__heading{flex-direction:row;align-items:center;justify-content:space-between}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
2915
2975
|
}
|
|
2916
2976
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, decorators: [{
|
|
2917
2977
|
type: Component,
|
|
2918
|
-
args: [{ selector: 'rolatech-offer-negotiation-section', imports: [MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (!isTerminal()) {\n<section class=\"
|
|
2978
|
+
args: [{ selector: 'rolatech-offer-negotiation-section', imports: [MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (!isTerminal()) {\n<section class=\"offer-negotiation-section\">\n <header class=\"offer-negotiation-section__header\">\n <div class=\"offer-negotiation-section__copy\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation controls</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">Respond to the latest offer</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n Keep the negotiation moving with one clear next action. Every response stays recorded in the offer history below.\n </p>\n </div>\n\n @if (f.lock(); as lk) {\n @if (lk.locked) {\n <div class=\"offer-negotiation-section__lock\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n }\n }\n </header>\n\n @if (f.error(); as err) {\n <div class=\"offer-negotiation-section__alert\">{{ err }}</div>\n }\n\n @if (f.editMode()) {\n <div class=\"offer-negotiation-section__edit-note\">\n Counter editing is active below. Review the updated terms, then send the counter-offer when ready.\n </div>\n }\n\n <div class=\"offer-negotiation-section__actions\">\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) {\n Working...\n } @else {\n {{ f.withdrawLabel() }}\n }\n </button>\n }\n\n <span class=\"offer-negotiation-section__spacer\"></span>\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) {\n Working...\n } @else {\n Cancel\n }\n </button>\n </div>\n\n @if (f.showLifecycleActions()) {\n <div class=\"offer-negotiation-section__secondary\">\n <div class=\"offer-negotiation-section__secondary-copy\">\n <span class=\"offer-negotiation-section__secondary-eyebrow\">Workflow steps</span>\n <h4 class=\"offer-negotiation-section__secondary-title\">Progress the deal after negotiation</h4>\n </div>\n\n <div class=\"offer-negotiation-section__secondary-actions\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n} @else {\n <section class=\"offer-negotiation-section offer-negotiation-section--terminal\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation closed</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">This offer is no longer awaiting action</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n The final state has been recorded. Use the history section below to review how the negotiation reached this outcome.\n </p>\n </section>\n}\n", styles: [":host{display:block}.offer-negotiation-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%);box-shadow:0 18px 42px -38px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.offer-negotiation-section__header,.offer-negotiation-section__copy,.offer-negotiation-section__secondary-copy{display:flex;flex-direction:column;gap:.45rem}.offer-negotiation-section__eyebrow,.offer-negotiation-section__secondary-eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .68rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-negotiation-section__heading{display:flex;flex-direction:column;gap:.75rem}.offer-negotiation-section__title,.offer-negotiation-section__secondary-title{margin:0;color:var(--rt-text-primary);font-size:1.08rem;line-height:1.2;font-weight:700}.offer-negotiation-section__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-negotiation-section__status,.offer-negotiation-section__lock{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.1rem;padding:.38rem .75rem;border-radius:999px;font-size:.8rem;font-weight:700}.offer-negotiation-section__status{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-raised-background, #ffffff);color:var(--rt-text-primary)}.offer-negotiation-section__lock{border:1px solid rgba(245,158,11,.24);background:#f59e0b1f;color:#a16207}.offer-negotiation-section__alert,.offer-negotiation-section__edit-note{padding:.85rem .95rem;border-radius:1rem;font-size:.9rem;line-height:1.55}.offer-negotiation-section__alert{border:1px solid rgba(239,68,68,.2);background:#fef2f2e6;color:#b91c1c}.offer-negotiation-section__edit-note{border:1px solid rgba(59,130,246,.16);background:#eff6ffe6;color:#1d4ed8}.offer-negotiation-section__actions,.offer-negotiation-section__secondary-actions{display:flex;flex-wrap:wrap;gap:.7rem}.offer-negotiation-section__spacer{flex:1 1 auto}.offer-negotiation-section__secondary{display:flex;flex-direction:column;gap:.85rem;padding-top:1rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-negotiation-section--terminal{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent)}@media(min-width:900px){.offer-negotiation-section__header{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-negotiation-section__heading{flex-direction:row;align-items:center;justify-content:space-between}}\n"] }]
|
|
2919
2979
|
}] });
|
|
2920
2980
|
|
|
2921
2981
|
class PropertyManageOfferDetailComponent extends BaseComponent {
|
|
@@ -3277,7 +3337,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
3277
3337
|
}]
|
|
3278
3338
|
}] });
|
|
3279
3339
|
|
|
3280
|
-
const MY_FORMATS$
|
|
3340
|
+
const MY_FORMATS$5 = {
|
|
3281
3341
|
parse: {
|
|
3282
3342
|
dateInput: 'YYYY-MM-DD',
|
|
3283
3343
|
},
|
|
@@ -3393,7 +3453,7 @@ class PropertyFilterBar {
|
|
|
3393
3453
|
useClass: MomentDateAdapter,
|
|
3394
3454
|
deps: [MAT_DATE_LOCALE],
|
|
3395
3455
|
},
|
|
3396
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
3456
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
|
|
3397
3457
|
], ngImport: i0, template: "<div class=\"px-2 mb-3 close\" [class.open]=\"show()\">\n <div class=\"flex flex-col h-min justify-center items-center gap-3\">\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Market</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Market\" [(ngModel)]=\"options().market\">\n @for (m of market; track m) {\n <mat-option [value]=\"m\"> {{ m }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Type</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Type\" [(ngModel)]=\"options().type\">\n @for (type of types | keyvalue; track type) {\n <mat-option [value]=\"type.key\"> {{ type.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Town</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"options().town\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\"> {{ town }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Available date</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\" class=\"z-[1000]\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"options().availableFrom\"\n (dateInput)=\"options().availableFrom = $event.value.format('YYYY-MM-DD')\"\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker panelClass=\"datepicker-on-top\" #startDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/3 overflow-hidden\">\n <div class=\"py-1\">Price range (\u00A3)</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().minPrice\">\n @for (opt of minOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().maxPrice\">\n @for (opt of maxOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/3\">\n <div class=\"py-1\">No. of bedrooms</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min</mat-label>\n <mat-select [(ngModel)]=\"options().minBedrooms\">\n @for (opt of minBedroom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max</mat-label>\n <mat-select [(ngModel)]=\"options().maxBedrooms\">\n @for (opt of maxBedRoom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"h-14 md:w-1/3 self-end w-full\">\n <button mat-flat-button (click)=\"onSearch()\" class=\"w-full min-h-14\">\n <a i18n>Search</a>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}.close{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.open{max-height:1000px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }], encapsulation: i0.ViewEncapsulation.None });
|
|
3398
3458
|
}
|
|
3399
3459
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyFilterBar, decorators: [{
|
|
@@ -3413,7 +3473,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
3413
3473
|
useClass: MomentDateAdapter,
|
|
3414
3474
|
deps: [MAT_DATE_LOCALE],
|
|
3415
3475
|
},
|
|
3416
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
3476
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
|
|
3417
3477
|
], encapsulation: ViewEncapsulation.None, template: "<div class=\"px-2 mb-3 close\" [class.open]=\"show()\">\n <div class=\"flex flex-col h-min justify-center items-center gap-3\">\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Market</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Market\" [(ngModel)]=\"options().market\">\n @for (m of market; track m) {\n <mat-option [value]=\"m\"> {{ m }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Type</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Type\" [(ngModel)]=\"options().type\">\n @for (type of types | keyvalue; track type) {\n <mat-option [value]=\"type.key\"> {{ type.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Town</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"options().town\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\"> {{ town }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Available date</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\" class=\"z-[1000]\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"options().availableFrom\"\n (dateInput)=\"options().availableFrom = $event.value.format('YYYY-MM-DD')\"\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker panelClass=\"datepicker-on-top\" #startDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/3 overflow-hidden\">\n <div class=\"py-1\">Price range (\u00A3)</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().minPrice\">\n @for (opt of minOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().maxPrice\">\n @for (opt of maxOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/3\">\n <div class=\"py-1\">No. of bedrooms</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min</mat-label>\n <mat-select [(ngModel)]=\"options().minBedrooms\">\n @for (opt of minBedroom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max</mat-label>\n <mat-select [(ngModel)]=\"options().maxBedrooms\">\n @for (opt of maxBedRoom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"h-14 md:w-1/3 self-end w-full\">\n <button mat-flat-button (click)=\"onSearch()\" class=\"w-full min-h-14\">\n <a i18n>Search</a>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}.close{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.open{max-height:1000px}\n"] }]
|
|
3418
3478
|
}], ctorParameters: () => [], propDecorators: { search: [{ type: i0.Output, args: ["search"] }], reset: [{ type: i0.Output, args: ["reset"] }], show: [{ type: i0.Input, args: [{ isSignal: true, alias: "show", required: false }] }, { type: i0.Output, args: ["showChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }, { type: i0.Output, args: ["optionsChange"] }] } });
|
|
3419
3479
|
|
|
@@ -4086,7 +4146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4086
4146
|
], template: "<rolatech-container>\n <div class=\"flex flex-col w-full\">\n @if (loading()) {\n <rolatech-property-media-skeleton></rolatech-property-media-skeleton>\n\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <rolatech-property-info-skeleton class=\"w-full md:w-2/3\"></rolatech-property-info-skeleton>\n <rolatech-property-agent-skeleton class=\"w-full md:w-1/3\"></rolatech-property-agent-skeleton>\n </div>\n } @else {\n @if (property(); as property) {\n <rolatech-property-media [media]=\"property.media ? property.media : []\"></rolatech-property-media>\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <div class=\"w-full md:w-2/3\">\n <rolatech-property-info [property]=\"property\" (wish)=\"onWish($event)\" [inWishList]=\"inWishList\">\n </rolatech-property-info>\n <!-- <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer> -->\n @if (features.length > 0) {\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n }\n @if (highlights.length > 0) {\n <rolatech-property-highlights [highlights]=\"highlights\"></rolatech-property-highlights>\n }\n @if (floorplans.length > 0) {\n <rolatech-property-floorplan [floorplans]=\"floorplans\"></rolatech-property-floorplan>\n }\n @if (property.location) {\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n }\n @if (videos() && videos().length > 0) {\n @for (item of videos(); track $index) {\n <rolatech-property-video [video]=\"item\"></rolatech-property-video>\n }\n }\n @if (epc && epc.currentScore) {\n <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc>\n }\n <div class=\"flex flex-col\">\n <div class=\"text-2xl font-bold pt-3\" i18n>Sections</div>\n @for (section of property.sections; track $index) {\n <rolatech-property-section [section]=\"section\"></rolatech-property-section>\n }\n <!-- <rolatech-comments [itemId]=\"property.id\"></rolatech-comments> -->\n </div>\n </div>\n <div class=\"w-full md:w-1/3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-property-actions\n [property]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-property-action-contact [email]=\"user.email\" [phone]=\"user.phone\"></rolatech-property-action-contact>\n }\n </rolatech-property-actions>\n </div>\n </div>\n }\n }\n </div>\n</rolatech-container>\n" }]
|
|
4087
4147
|
}], ctorParameters: () => [] });
|
|
4088
4148
|
|
|
4089
|
-
const MY_FORMATS$
|
|
4149
|
+
const MY_FORMATS$4 = {
|
|
4090
4150
|
parse: {
|
|
4091
4151
|
dateInput: 'YYYY-MM-DD',
|
|
4092
4152
|
},
|
|
@@ -4101,7 +4161,6 @@ class PropertyViewingTimeComponent {
|
|
|
4101
4161
|
minDate = new Date();
|
|
4102
4162
|
maxDate = new Date();
|
|
4103
4163
|
output = output();
|
|
4104
|
-
select = output();
|
|
4105
4164
|
proposedTime = input({
|
|
4106
4165
|
date: '',
|
|
4107
4166
|
time: '',
|
|
@@ -4111,18 +4170,56 @@ class PropertyViewingTimeComponent {
|
|
|
4111
4170
|
this.minDate.setDate(this.minDate.getDate() + 1);
|
|
4112
4171
|
this.maxDate.setDate(this.maxDate.getDate() + 7);
|
|
4113
4172
|
}
|
|
4114
|
-
|
|
4115
|
-
this.
|
|
4173
|
+
onDateInput(event) {
|
|
4174
|
+
this.updateSlot({
|
|
4175
|
+
date: this.formatDateValue(event.value),
|
|
4176
|
+
});
|
|
4177
|
+
}
|
|
4178
|
+
onTimeChange(time) {
|
|
4179
|
+
this.updateSlot({
|
|
4180
|
+
time: time ?? '',
|
|
4181
|
+
});
|
|
4182
|
+
}
|
|
4183
|
+
updateSlot(changes) {
|
|
4184
|
+
const current = this.proposedTime() ?? {
|
|
4185
|
+
date: '',
|
|
4186
|
+
time: '',
|
|
4187
|
+
};
|
|
4188
|
+
const next = {
|
|
4189
|
+
...current,
|
|
4190
|
+
...changes,
|
|
4191
|
+
};
|
|
4192
|
+
if (current.date === next.date && current.time === next.time) {
|
|
4193
|
+
return;
|
|
4194
|
+
}
|
|
4195
|
+
current.date = next.date;
|
|
4196
|
+
current.time = next.time;
|
|
4197
|
+
this.output.emit(next);
|
|
4198
|
+
}
|
|
4199
|
+
formatDateValue(value) {
|
|
4200
|
+
if (!value) {
|
|
4201
|
+
return '';
|
|
4202
|
+
}
|
|
4203
|
+
if (typeof value === 'object' && value !== null && 'format' in value && typeof value.format === 'function') {
|
|
4204
|
+
return value.format('YYYY-MM-DD');
|
|
4205
|
+
}
|
|
4206
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
4207
|
+
const year = value.getFullYear();
|
|
4208
|
+
const month = String(value.getMonth() + 1).padStart(2, '0');
|
|
4209
|
+
const day = String(value.getDate()).padStart(2, '0');
|
|
4210
|
+
return `${year}-${month}-${day}`;
|
|
4211
|
+
}
|
|
4212
|
+
return String(value);
|
|
4116
4213
|
}
|
|
4117
4214
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4118
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingTimeComponent, isStandalone: true, selector: "rolatech-property-viewing-time", inputs: { proposedTime: { classPropertyName: "proposedTime", publicName: "proposedTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { output: "output"
|
|
4215
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingTimeComponent, isStandalone: true, selector: "rolatech-property-viewing-time", inputs: { proposedTime: { classPropertyName: "proposedTime", publicName: "proposedTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { output: "output" }, providers: [
|
|
4119
4216
|
{
|
|
4120
4217
|
provide: DateAdapter,
|
|
4121
4218
|
useClass: MomentDateAdapter,
|
|
4122
4219
|
deps: [MAT_DATE_LOCALE],
|
|
4123
4220
|
},
|
|
4124
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
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 [
|
|
4221
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
|
|
4222
|
+
], ngImport: i0, template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [ngModel]=\"proposedTime().date || null\"\n (dateInput)=\"onDateInput($event)\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [ngModel]=\"proposedTime().time || null\" (ngModelChange)=\"onTimeChange($event)\" required>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }] });
|
|
4126
4223
|
}
|
|
4127
4224
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, decorators: [{
|
|
4128
4225
|
type: Component,
|
|
@@ -4141,9 +4238,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4141
4238
|
useClass: MomentDateAdapter,
|
|
4142
4239
|
deps: [MAT_DATE_LOCALE],
|
|
4143
4240
|
},
|
|
4144
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
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"] }],
|
|
4241
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
|
|
4242
|
+
], template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [ngModel]=\"proposedTime().date || null\"\n (dateInput)=\"onDateInput($event)\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [ngModel]=\"proposedTime().time || null\" (ngModelChange)=\"onTimeChange($event)\" required>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
|
|
4243
|
+
}], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
|
|
4147
4244
|
|
|
4148
4245
|
class PropertyIntentShell {
|
|
4149
4246
|
property = input(null, ...(ngDevMode ? [{ debugName: "property" }] : []));
|
|
@@ -4266,7 +4363,7 @@ class PropertyViewingRequestComponent extends BaseComponent {
|
|
|
4266
4363
|
this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
|
|
4267
4364
|
}
|
|
4268
4365
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4269
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", usesInheritance: true, ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Buyer workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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" }] });
|
|
4366
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", usesInheritance: true, ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Market workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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
4367
|
}
|
|
4271
4368
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, decorators: [{
|
|
4272
4369
|
type: Component,
|
|
@@ -4284,7 +4381,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4284
4381
|
PropertyViewingTimeComponent,
|
|
4285
4382
|
PropertyIntentShell,
|
|
4286
4383
|
PropertyMarketBreadcrumbComponent,
|
|
4287
|
-
], template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? '
|
|
4384
|
+
], template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Market workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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"] }]
|
|
4288
4385
|
}], ctorParameters: () => [] });
|
|
4289
4386
|
|
|
4290
4387
|
class PropertyOfferIndexComponent extends BaseComponent {
|
|
@@ -4533,6 +4630,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4533
4630
|
|
|
4534
4631
|
class OfferHeaderCard {
|
|
4535
4632
|
ctx = inject(OfferDetailContext);
|
|
4633
|
+
offerStatusLabel = offerStatusLabel;
|
|
4536
4634
|
ok = new Set([
|
|
4537
4635
|
PropertyOfferStatus$1.ACCEPTED,
|
|
4538
4636
|
PropertyOfferStatus$1.REFERENCES_PASSED,
|
|
@@ -4567,15 +4665,15 @@ class OfferHeaderCard {
|
|
|
4567
4665
|
]);
|
|
4568
4666
|
statusBadgeClass(status) {
|
|
4569
4667
|
if (!status)
|
|
4570
|
-
return '
|
|
4668
|
+
return 'offer-header-card__status offer-header-card__status--neutral';
|
|
4571
4669
|
if (this.ok.has(status))
|
|
4572
|
-
return '
|
|
4670
|
+
return 'offer-header-card__status offer-header-card__status--positive';
|
|
4573
4671
|
if (this.warn.has(status))
|
|
4574
|
-
return '
|
|
4672
|
+
return 'offer-header-card__status offer-header-card__status--warning';
|
|
4575
4673
|
if (this.bad.has(status))
|
|
4576
|
-
return '
|
|
4674
|
+
return 'offer-header-card__status offer-header-card__status--negative';
|
|
4577
4675
|
// DRAFT or unknown
|
|
4578
|
-
return '
|
|
4676
|
+
return 'offer-header-card__status offer-header-card__status--neutral';
|
|
4579
4677
|
}
|
|
4580
4678
|
statusHint(status) {
|
|
4581
4679
|
if (!status)
|
|
@@ -4635,270 +4733,215 @@ class OfferHeaderCard {
|
|
|
4635
4733
|
return '';
|
|
4636
4734
|
}
|
|
4637
4735
|
}
|
|
4736
|
+
typeLabel(value) {
|
|
4737
|
+
if (!value) {
|
|
4738
|
+
return 'Offer';
|
|
4739
|
+
}
|
|
4740
|
+
return `${String(value).toLowerCase().replace(/^\w/, (char) => char.toUpperCase())} offer`;
|
|
4741
|
+
}
|
|
4638
4742
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4639
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHeaderCard, isStandalone: true, selector: "rolatech-offer-header-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4743
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHeaderCard, isStandalone: true, selector: "rolatech-offer-header-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-header-card\">\n <div class=\"offer-header-card__body\">\n <div class=\"offer-header-card__copy\">\n <span class=\"offer-header-card__eyebrow\">{{ typeLabel(o.type) }}</span>\n\n <div class=\"offer-header-card__heading\">\n <div class=\"offer-header-card__heading-copy\">\n <h2 class=\"offer-header-card__title\">Offer #{{ o.id }}</h2>\n <p class=\"offer-header-card__description\">{{ statusHint(o.status) }}</p>\n </div>\n\n <span [ngClass]=\"statusBadgeClass(o.status)\">{{ offerStatusLabel(o.status) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__facts\">\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Offer type</span>\n <strong class=\"offer-header-card__fact-value\">{{ typeLabel(o.type) }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Invoice style</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">{{ o.type === 'RENTAL' ? 'Applicants' : 'Payment method' }}</span>\n <strong class=\"offer-header-card__fact-value\">\n @if (o.type === 'RENTAL') {\n {{ o.tenants?.length ?? 0 }}\n } @else {\n {{ ctx.asText(o.sale?.paymentMethod) }}\n }\n </strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Property reference</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.asText(o.propertyId) }}</strong>\n </article>\n </div>\n\n <div class=\"offer-header-card__meta\">\n <span class=\"offer-header-card__meta-chip\">Applicant ID {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Agent ID {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Property ID {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__actions\">\n <ng-content></ng-content>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-header-card{display:grid;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 44%),linear-gradient(140deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%)),var(--rt-base-background, #ffffff);box-shadow:0 22px 52px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.offer-header-card__body{display:flex;flex-direction:column;gap:1rem}.offer-header-card__copy,.offer-header-card__heading-copy{display:flex;flex-direction:column;gap:.55rem}.offer-header-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.38rem .72rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-header-card__heading{display:flex;flex-direction:column;gap:.85rem}.offer-header-card__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.3rem,2vw,1.75rem);line-height:1.1;font-weight:800}.offer-header-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-header-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.4rem;padding:.45rem .85rem;border:1px solid transparent;border-radius:999px;font-size:.85rem;font-weight:700}.offer-header-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-header-card__status--warning{border-color:#f59e0b47;background:#f59e0b1f;color:#a16207}.offer-header-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-header-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);color:var(--rt-text-secondary)}.offer-header-card__facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-header-card__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-header-card__fact-label{color:var(--rt-text-secondary);font-size:.78rem}.offer-header-card__fact-value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.35}.offer-header-card__meta{display:flex;flex-wrap:wrap;gap:.55rem}.offer-header-card__meta-chip{display:inline-flex;align-items:center;min-height:2rem;padding:.4rem .72rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-header-card__actions{display:flex;align-items:flex-start;justify-content:flex-end}@media(min-width:960px){.offer-header-card{grid-template-columns:minmax(0,1.7fr) auto;align-items:start}.offer-header-card__heading{flex-direction:row;align-items:flex-start;justify-content:space-between}.offer-header-card__facts{grid-template-columns:repeat(4,minmax(0,1fr))}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
4640
4744
|
}
|
|
4641
4745
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, decorators: [{
|
|
4642
4746
|
type: Component,
|
|
4643
|
-
args: [{ selector: 'rolatech-offer-header-card', imports: [NgClass], template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4747
|
+
args: [{ selector: 'rolatech-offer-header-card', imports: [NgClass], template: "@if (ctx.offer(); as o) {\n<section class=\"offer-header-card\">\n <div class=\"offer-header-card__body\">\n <div class=\"offer-header-card__copy\">\n <span class=\"offer-header-card__eyebrow\">{{ typeLabel(o.type) }}</span>\n\n <div class=\"offer-header-card__heading\">\n <div class=\"offer-header-card__heading-copy\">\n <h2 class=\"offer-header-card__title\">Offer #{{ o.id }}</h2>\n <p class=\"offer-header-card__description\">{{ statusHint(o.status) }}</p>\n </div>\n\n <span [ngClass]=\"statusBadgeClass(o.status)\">{{ offerStatusLabel(o.status) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__facts\">\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Offer type</span>\n <strong class=\"offer-header-card__fact-value\">{{ typeLabel(o.type) }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Invoice style</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">{{ o.type === 'RENTAL' ? 'Applicants' : 'Payment method' }}</span>\n <strong class=\"offer-header-card__fact-value\">\n @if (o.type === 'RENTAL') {\n {{ o.tenants?.length ?? 0 }}\n } @else {\n {{ ctx.asText(o.sale?.paymentMethod) }}\n }\n </strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Property reference</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.asText(o.propertyId) }}</strong>\n </article>\n </div>\n\n <div class=\"offer-header-card__meta\">\n <span class=\"offer-header-card__meta-chip\">Applicant ID {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Agent ID {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Property ID {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__actions\">\n <ng-content></ng-content>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-header-card{display:grid;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 44%),linear-gradient(140deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%)),var(--rt-base-background, #ffffff);box-shadow:0 22px 52px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.offer-header-card__body{display:flex;flex-direction:column;gap:1rem}.offer-header-card__copy,.offer-header-card__heading-copy{display:flex;flex-direction:column;gap:.55rem}.offer-header-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.38rem .72rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-header-card__heading{display:flex;flex-direction:column;gap:.85rem}.offer-header-card__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.3rem,2vw,1.75rem);line-height:1.1;font-weight:800}.offer-header-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-header-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.4rem;padding:.45rem .85rem;border:1px solid transparent;border-radius:999px;font-size:.85rem;font-weight:700}.offer-header-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-header-card__status--warning{border-color:#f59e0b47;background:#f59e0b1f;color:#a16207}.offer-header-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-header-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);color:var(--rt-text-secondary)}.offer-header-card__facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-header-card__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-header-card__fact-label{color:var(--rt-text-secondary);font-size:.78rem}.offer-header-card__fact-value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.35}.offer-header-card__meta{display:flex;flex-wrap:wrap;gap:.55rem}.offer-header-card__meta-chip{display:inline-flex;align-items:center;min-height:2rem;padding:.4rem .72rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-header-card__actions{display:flex;align-items:flex-start;justify-content:flex-end}@media(min-width:960px){.offer-header-card{grid-template-columns:minmax(0,1.7fr) auto;align-items:start}.offer-header-card__heading{flex-direction:row;align-items:flex-start;justify-content:space-between}.offer-header-card__facts{grid-template-columns:repeat(4,minmax(0,1fr))}}\n"] }]
|
|
4644
4748
|
}] });
|
|
4645
4749
|
|
|
4646
4750
|
class OfferSideSummaryCard {
|
|
4647
4751
|
ctx = inject(OfferDetailContext);
|
|
4752
|
+
statusLabel = offerStatusLabel;
|
|
4648
4753
|
salePaymentMethodLabel = {
|
|
4649
4754
|
CASH: 'Cash',
|
|
4650
4755
|
MORTGAGE: 'Mortgage',
|
|
4651
4756
|
};
|
|
4757
|
+
typeLabel(value) {
|
|
4758
|
+
if (!value) {
|
|
4759
|
+
return 'Offer';
|
|
4760
|
+
}
|
|
4761
|
+
return `${String(value).toLowerCase().replace(/^\w/, (char) => char.toUpperCase())} offer`;
|
|
4762
|
+
}
|
|
4763
|
+
statusToneClass(status) {
|
|
4764
|
+
switch (status) {
|
|
4765
|
+
case PropertyOfferStatus$1.ACCEPTED:
|
|
4766
|
+
case PropertyOfferStatus$1.REFERENCES_PASSED:
|
|
4767
|
+
case PropertyOfferStatus$1.CONTRACT_SIGNED:
|
|
4768
|
+
case PropertyOfferStatus$1.MOVE_IN_PAYMENT_PAID:
|
|
4769
|
+
case PropertyOfferStatus$1.COMPLETED:
|
|
4770
|
+
return 'offer-side-summary-card__status offer-side-summary-card__status--positive';
|
|
4771
|
+
case PropertyOfferStatus$1.REJECTED:
|
|
4772
|
+
case PropertyOfferStatus$1.REFERENCES_FAILED:
|
|
4773
|
+
case PropertyOfferStatus$1.CONTRACT_FAILED:
|
|
4774
|
+
case PropertyOfferStatus$1.CANCELLED:
|
|
4775
|
+
case PropertyOfferStatus$1.FAILED:
|
|
4776
|
+
case PropertyOfferStatus$1.EXPIRED:
|
|
4777
|
+
return 'offer-side-summary-card__status offer-side-summary-card__status--negative';
|
|
4778
|
+
default:
|
|
4779
|
+
return 'offer-side-summary-card__status offer-side-summary-card__status--neutral';
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
leadAmount(offer) {
|
|
4783
|
+
if (offer.type === 'RENTAL') {
|
|
4784
|
+
return offer.rentalTerms?.amount ?? offer.item.amount;
|
|
4785
|
+
}
|
|
4786
|
+
return offer.sale?.amount ?? offer.item.amount;
|
|
4787
|
+
}
|
|
4788
|
+
nextStepCopy(offer) {
|
|
4789
|
+
switch (offer.status) {
|
|
4790
|
+
case PropertyOfferStatus$1.SUBMITTED:
|
|
4791
|
+
case PropertyOfferStatus$1.VIEWED_BY_AGENT:
|
|
4792
|
+
case PropertyOfferStatus$1.VIEWED_BY_TENANT:
|
|
4793
|
+
return 'The latest offer is waiting for a response.';
|
|
4794
|
+
case PropertyOfferStatus$1.COUNTERED_BY_AGENT:
|
|
4795
|
+
case PropertyOfferStatus$1.COUNTERED_BY_TENANT:
|
|
4796
|
+
return 'Compare the latest version against the previous one before responding.';
|
|
4797
|
+
case PropertyOfferStatus$1.ACCEPTED:
|
|
4798
|
+
case PropertyOfferStatus$1.UNDER_OFFER:
|
|
4799
|
+
return 'The negotiation has moved forward into the next workflow step.';
|
|
4800
|
+
case PropertyOfferStatus$1.REFERENCING:
|
|
4801
|
+
return 'Referencing checks are in progress.';
|
|
4802
|
+
case PropertyOfferStatus$1.COMPLETED:
|
|
4803
|
+
return 'The offer flow is complete and kept here as a record.';
|
|
4804
|
+
default:
|
|
4805
|
+
return 'Use this summary to keep the main terms and references visible while reviewing the detail page.';
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4652
4808
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4653
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4809
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-side-summary-card\">\n <div class=\"offer-side-summary-card__hero\">\n <span class=\"offer-side-summary-card__eyebrow\">Quick summary</span>\n <div class=\"offer-side-summary-card__amount\">{{ leadAmount(o) | price }}</div>\n <p class=\"offer-side-summary-card__description\">{{ nextStepCopy(o) }}</p>\n <span [class]=\"statusToneClass(o.status)\">{{ statusLabel(o.status) }}</span>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Offer details</h3>\n <div class=\"offer-side-summary-card__rows\">\n <div class=\"offer-side-summary-card__row\">\n <span>Type</span>\n <strong>{{ typeLabel(o.type) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Invoice</span>\n <strong>{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Listing amount</span>\n <strong>{{ o.item.amount | price }}</strong>\n </div>\n @if (ctx.isRental()) {\n <div class=\"offer-side-summary-card__row\">\n <span>Applicants</span>\n <strong>{{ o.tenants?.length ?? 0 }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Move-in</span>\n <strong>{{ o.rentalTerms?.moveInDate ? (o.rentalTerms?.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </div>\n } @else {\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer method</span>\n <strong>{{ salePaymentMethodLabel[o.sale?.paymentMethod!] || ctx.asText(o.sale?.paymentMethod) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer</span>\n <strong>{{ o.sale?.buyerName ?? '\u2014' }}</strong>\n </div>\n }\n </div>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Route references</h3>\n <div class=\"offer-side-summary-card__chips\">\n <span class=\"offer-side-summary-card__chip\">User {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Agent {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Property {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-side-summary-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.05rem;background:linear-gradient(150deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%))}.offer-side-summary-card__hero,.offer-side-summary-card__section{display:flex;flex-direction:column;gap:.6rem}.offer-side-summary-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-side-summary-card__amount{color:var(--rt-text-primary);font-size:clamp(1.55rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-side-summary-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-side-summary-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2rem;padding:.35rem .7rem;border:1px solid transparent;border-radius:999px;font-size:.8rem;font-weight:700}.offer-side-summary-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-side-summary-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-side-summary-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-base-background, #ffffff);color:var(--rt-text-primary)}.offer-side-summary-card__section{padding-top:.9rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-side-summary-card__section-title{margin:0;color:var(--rt-text-primary);font-size:.96rem;font-weight:720}.offer-side-summary-card__rows{display:flex;flex-direction:column;gap:.7rem}.offer-side-summary-card__row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;color:var(--rt-text-secondary);font-size:.88rem}.offer-side-summary-card__row strong{color:var(--rt-text-primary);text-align:right}.offer-side-summary-card__chips{display:flex;flex-wrap:wrap;gap:.5rem}.offer-side-summary-card__chip{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
4654
4810
|
}
|
|
4655
4811
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, decorators: [{
|
|
4656
4812
|
type: Component,
|
|
4657
|
-
args: [{ selector: 'rolatech-offer-side-summary-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4813
|
+
args: [{ selector: 'rolatech-offer-side-summary-card', imports: [CommonModule, PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-side-summary-card\">\n <div class=\"offer-side-summary-card__hero\">\n <span class=\"offer-side-summary-card__eyebrow\">Quick summary</span>\n <div class=\"offer-side-summary-card__amount\">{{ leadAmount(o) | price }}</div>\n <p class=\"offer-side-summary-card__description\">{{ nextStepCopy(o) }}</p>\n <span [class]=\"statusToneClass(o.status)\">{{ statusLabel(o.status) }}</span>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Offer details</h3>\n <div class=\"offer-side-summary-card__rows\">\n <div class=\"offer-side-summary-card__row\">\n <span>Type</span>\n <strong>{{ typeLabel(o.type) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Invoice</span>\n <strong>{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Listing amount</span>\n <strong>{{ o.item.amount | price }}</strong>\n </div>\n @if (ctx.isRental()) {\n <div class=\"offer-side-summary-card__row\">\n <span>Applicants</span>\n <strong>{{ o.tenants?.length ?? 0 }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Move-in</span>\n <strong>{{ o.rentalTerms?.moveInDate ? (o.rentalTerms?.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </div>\n } @else {\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer method</span>\n <strong>{{ salePaymentMethodLabel[o.sale?.paymentMethod!] || ctx.asText(o.sale?.paymentMethod) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer</span>\n <strong>{{ o.sale?.buyerName ?? '\u2014' }}</strong>\n </div>\n }\n </div>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Route references</h3>\n <div class=\"offer-side-summary-card__chips\">\n <span class=\"offer-side-summary-card__chip\">User {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Agent {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Property {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-side-summary-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.05rem;background:linear-gradient(150deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%))}.offer-side-summary-card__hero,.offer-side-summary-card__section{display:flex;flex-direction:column;gap:.6rem}.offer-side-summary-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-side-summary-card__amount{color:var(--rt-text-primary);font-size:clamp(1.55rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-side-summary-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-side-summary-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2rem;padding:.35rem .7rem;border:1px solid transparent;border-radius:999px;font-size:.8rem;font-weight:700}.offer-side-summary-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-side-summary-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-side-summary-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-base-background, #ffffff);color:var(--rt-text-primary)}.offer-side-summary-card__section{padding-top:.9rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-side-summary-card__section-title{margin:0;color:var(--rt-text-primary);font-size:.96rem;font-weight:720}.offer-side-summary-card__rows{display:flex;flex-direction:column;gap:.7rem}.offer-side-summary-card__row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;color:var(--rt-text-secondary);font-size:.88rem}.offer-side-summary-card__row strong{color:var(--rt-text-primary);text-align:right}.offer-side-summary-card__chips{display:flex;flex-wrap:wrap;gap:.5rem}.offer-side-summary-card__chip{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}\n"] }]
|
|
4658
4814
|
}] });
|
|
4659
4815
|
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
}
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
};
|
|
4816
|
+
class PropertyWorkspaceShell {
|
|
4817
|
+
eyebrow = input('Workspace', ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
|
|
4818
|
+
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
4819
|
+
description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
|
|
4820
|
+
stats = input([], ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
4821
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4822
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyWorkspaceShell, isStandalone: true, selector: "rolatech-property-workspace-shell", inputs: { eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, stats: { classPropertyName: "stats", publicName: "stats", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
4823
|
+
}
|
|
4824
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, decorators: [{
|
|
4825
|
+
type: Component,
|
|
4826
|
+
args: [{ selector: 'rolatech-property-workspace-shell', imports: [CommonModule], template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"] }]
|
|
4827
|
+
}], propDecorators: { eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], stats: [{ type: i0.Input, args: [{ isSignal: true, alias: "stats", required: false }] }] } });
|
|
4828
|
+
|
|
4671
4829
|
class PropertyOfferDetailComponent extends BaseComponent {
|
|
4672
|
-
propertyService = inject(PropertyService);
|
|
4673
4830
|
propertyOfferService = inject(PropertyOfferService);
|
|
4674
|
-
propertyOfferCounterService = inject(PropertyOfferCounterService);
|
|
4675
4831
|
f = inject(PropertyOfferNegotiationFacade);
|
|
4676
|
-
authUserService = inject(AuthUserService);
|
|
4677
|
-
paymentService = inject(PaymentService);
|
|
4678
4832
|
ctx = inject(OfferDetailContext);
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
});
|
|
4710
|
-
this.route.queryParams.subscribe(async (params) => {
|
|
4711
|
-
const sessionId = params['session_id'];
|
|
4712
|
-
if (sessionId) {
|
|
4713
|
-
this.propertyOfferService.checkOfferPaymentStatus(this.id, sessionId).subscribe({
|
|
4714
|
-
next: (res) => {
|
|
4715
|
-
if (res.status === 'paid') {
|
|
4716
|
-
this.offer.status = PropertyOfferStatus.HOLDING_DEPOSIT_PAID;
|
|
4717
|
-
// Show success UI
|
|
4718
|
-
}
|
|
4719
|
-
},
|
|
4720
|
-
});
|
|
4721
|
-
}
|
|
4722
|
-
});
|
|
4723
|
-
}
|
|
4724
|
-
ngOnDestroy() {
|
|
4725
|
-
this.checkouting = false;
|
|
4726
|
-
}
|
|
4727
|
-
get(id) {
|
|
4728
|
-
this.propertyOfferService.getOffer(id).subscribe({
|
|
4729
|
-
next: (res) => {
|
|
4730
|
-
this.offer = res.data;
|
|
4731
|
-
this.getProperty(this.offer.propertyId);
|
|
4732
|
-
this.calculateDeposit();
|
|
4733
|
-
// this.findAgentInfo();
|
|
4734
|
-
},
|
|
4735
|
-
});
|
|
4736
|
-
}
|
|
4737
|
-
getViewByTenant(id) {
|
|
4738
|
-
this.propertyOfferCounterService.getNegotiationViewByTenant(id).subscribe({
|
|
4739
|
-
next: (res) => {
|
|
4740
|
-
this.view = res.data;
|
|
4741
|
-
},
|
|
4742
|
-
});
|
|
4743
|
-
}
|
|
4744
|
-
timeline() {
|
|
4745
|
-
this.info = true;
|
|
4746
|
-
this.loadingTimeline = true;
|
|
4747
|
-
this.propertyOfferService.offerTimeline(this.id).subscribe({
|
|
4748
|
-
next: (res) => {
|
|
4749
|
-
this.timelineData = res.data;
|
|
4750
|
-
this.loadingTimeline = false;
|
|
4751
|
-
},
|
|
4752
|
-
error: (error) => {
|
|
4753
|
-
this.loadingTimeline = false;
|
|
4754
|
-
},
|
|
4755
|
-
});
|
|
4756
|
-
}
|
|
4757
|
-
getProperty(propertyId) {
|
|
4758
|
-
this.propertyService.get(propertyId).subscribe({
|
|
4759
|
-
next: (res) => {
|
|
4760
|
-
this.property = res.data;
|
|
4761
|
-
this.getAgentPublicInfo(this.property.agentId);
|
|
4762
|
-
},
|
|
4763
|
-
});
|
|
4764
|
-
}
|
|
4765
|
-
getAgentPublicInfo(agentId) {
|
|
4766
|
-
this.authUserService.getPublicUserInfo(agentId).subscribe({
|
|
4767
|
-
next: (res) => {
|
|
4768
|
-
this.agent = res;
|
|
4769
|
-
},
|
|
4770
|
-
});
|
|
4771
|
-
}
|
|
4772
|
-
cancel() {
|
|
4773
|
-
this.propertyOfferService.cancelOffer(this.id).subscribe({
|
|
4774
|
-
next: (res) => {
|
|
4775
|
-
this.offer.status = 'CANCELLED';
|
|
4776
|
-
this.snackBarService.open('Cancelled');
|
|
4777
|
-
},
|
|
4778
|
-
error: (error) => {
|
|
4779
|
-
this.snackBarService.open(error.message);
|
|
4780
|
-
},
|
|
4781
|
-
});
|
|
4782
|
-
}
|
|
4783
|
-
calculateDeposit() {
|
|
4784
|
-
const price = this.offer.item.amount;
|
|
4785
|
-
this.holdingDeposit = ((price * 12) / 52).toFixed(2);
|
|
4786
|
-
this.securityDeposit = (((price * 12) / 52) * 5).toFixed(2);
|
|
4787
|
-
}
|
|
4788
|
-
holdingDepositCheckout() {
|
|
4789
|
-
this.checkouting = true;
|
|
4790
|
-
const data = {
|
|
4791
|
-
businessType: 'OFFER',
|
|
4792
|
-
businessId: this.id,
|
|
4793
|
-
provider: 'STRIPE',
|
|
4794
|
-
method: 'STRIPE_CHECKOUT',
|
|
4795
|
-
purpose: 'HOLDING_DEPOSIT',
|
|
4796
|
-
};
|
|
4797
|
-
this.paymentService
|
|
4798
|
-
.createPaymentIntent(data)
|
|
4799
|
-
.pipe(switchMap((res) => {
|
|
4800
|
-
const intent = {
|
|
4801
|
-
intentId: res.data.id,
|
|
4802
|
-
provider: 'STRIPE',
|
|
4803
|
-
method: 'STRIPE_CHECKOUT',
|
|
4804
|
-
successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
|
|
4805
|
-
cancelUrl: window.location.href,
|
|
4806
|
-
};
|
|
4807
|
-
return this.paymentService.createPayment(intent);
|
|
4808
|
-
}))
|
|
4809
|
-
.subscribe({
|
|
4810
|
-
next: (res) => {
|
|
4811
|
-
this.checkouting = false;
|
|
4812
|
-
const { checkoutUrl } = res.data;
|
|
4813
|
-
window.open(checkoutUrl, '_blank');
|
|
4814
|
-
},
|
|
4815
|
-
error: (error) => {
|
|
4816
|
-
this.checkouting = false;
|
|
4817
|
-
},
|
|
4818
|
-
});
|
|
4819
|
-
}
|
|
4820
|
-
securityDepositCheckout() {
|
|
4821
|
-
this.checkouting = true;
|
|
4822
|
-
const data = {
|
|
4823
|
-
successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
|
|
4824
|
-
cancelUrl: window.location.href,
|
|
4825
|
-
};
|
|
4826
|
-
this.propertyOfferService.createSecurityDepositCheckout(this.id, data).subscribe({
|
|
4827
|
-
next: (res) => {
|
|
4828
|
-
window.location.href = res.data.checkoutUrl;
|
|
4829
|
-
},
|
|
4830
|
-
error: (error) => {
|
|
4831
|
-
this.checkouting = false;
|
|
4833
|
+
mode = input('tenant', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
4834
|
+
eyebrow = input(null, ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
|
|
4835
|
+
title = input(null, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
4836
|
+
description = input(null, ...(ngDevMode ? [{ debugName: "description" }] : []));
|
|
4837
|
+
backLabel = input(null, ...(ngDevMode ? [{ debugName: "backLabel" }] : []));
|
|
4838
|
+
currentOfferId = this.id ?? null;
|
|
4839
|
+
statusLabel = computed(() => offerStatusLabel(this.ctx.offer()?.status), ...(ngDevMode ? [{ debugName: "statusLabel" }] : []));
|
|
4840
|
+
resolvedEyebrow = computed(() => this.eyebrow() ?? (this.mode() === 'agent' ? 'Market workflow' : 'My applications'), ...(ngDevMode ? [{ debugName: "resolvedEyebrow" }] : []));
|
|
4841
|
+
resolvedTitle = computed(() => this.title() ?? (this.mode() === 'agent' ? 'Offer detail' : 'Offer detail'), ...(ngDevMode ? [{ debugName: "resolvedTitle" }] : []));
|
|
4842
|
+
resolvedDescription = computed(() => this.description() ??
|
|
4843
|
+
(this.mode() === 'agent'
|
|
4844
|
+
? 'Track buyer-side negotiations, compare counter-offers, and keep the deal moving from one shared workspace.'
|
|
4845
|
+
: 'Review every negotiation update, compare offer versions, and keep the next step close at hand.'), ...(ngDevMode ? [{ debugName: "resolvedDescription" }] : []));
|
|
4846
|
+
resolvedBackLabel = computed(() => this.backLabel() ?? (this.mode() === 'agent' ? 'Back to buyer offers' : 'Back to offers'), ...(ngDevMode ? [{ debugName: "resolvedBackLabel" }] : []));
|
|
4847
|
+
stats = computed(() => {
|
|
4848
|
+
const offer = this.ctx.offer();
|
|
4849
|
+
if (!offer) {
|
|
4850
|
+
return [
|
|
4851
|
+
{ label: 'Status', value: 'Loading', hint: 'Checking the current negotiation state' },
|
|
4852
|
+
{ label: 'Type', value: 'Loading', hint: 'Resolving the submitted offer type' },
|
|
4853
|
+
{ label: 'Applicants', value: 'Loading', hint: 'Looking up the latest submitted party count' },
|
|
4854
|
+
{ label: 'Offer amount', value: 'Loading', hint: 'Fetching the latest property snapshot' },
|
|
4855
|
+
];
|
|
4856
|
+
}
|
|
4857
|
+
const isRental = offer.type === 'RENTAL';
|
|
4858
|
+
return [
|
|
4859
|
+
{
|
|
4860
|
+
label: 'Status',
|
|
4861
|
+
value: this.statusLabel(),
|
|
4862
|
+
hint: this.mode() === 'agent' ? 'Latest buyer-side negotiation state' : 'Latest response from the listing side',
|
|
4832
4863
|
},
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4864
|
+
{
|
|
4865
|
+
label: 'Type',
|
|
4866
|
+
value: this.humanize(offer.type),
|
|
4867
|
+
hint: isRental ? 'Rental negotiation workflow' : 'Sale negotiation workflow',
|
|
4868
|
+
},
|
|
4869
|
+
isRental
|
|
4870
|
+
? {
|
|
4871
|
+
label: 'Applicants',
|
|
4872
|
+
value: offer.tenants?.length ?? 0,
|
|
4873
|
+
hint: 'People included in this submission',
|
|
4874
|
+
}
|
|
4875
|
+
: {
|
|
4876
|
+
label: 'Funding',
|
|
4877
|
+
value: this.humanize(offer.sale?.paymentMethod),
|
|
4878
|
+
hint: 'Declared buyer payment method',
|
|
4879
|
+
},
|
|
4880
|
+
{
|
|
4881
|
+
label: 'Offer amount',
|
|
4882
|
+
value: this.formatMoney(offer.item?.amount),
|
|
4883
|
+
hint: offer.item?.title || 'Property snapshot attached to this offer',
|
|
4840
4884
|
},
|
|
4841
|
-
|
|
4842
|
-
}
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
this.f.reject(notes);
|
|
4850
|
-
}
|
|
4851
|
-
});
|
|
4852
|
-
}
|
|
4853
|
-
openReferenceFailedDialog() {
|
|
4854
|
-
const ref = this.dialog.open(RejectDialogComponent, {
|
|
4855
|
-
width: '420px',
|
|
4856
|
-
});
|
|
4857
|
-
ref.afterClosed().subscribe((notes) => {
|
|
4858
|
-
if (notes) {
|
|
4859
|
-
this.f.markReferencesFailed(notes);
|
|
4885
|
+
];
|
|
4886
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
4887
|
+
ngOnInit() {
|
|
4888
|
+
this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
|
4889
|
+
const id = params.get('id');
|
|
4890
|
+
this.currentOfferId = id;
|
|
4891
|
+
if (!id) {
|
|
4892
|
+
return;
|
|
4860
4893
|
}
|
|
4894
|
+
this.ctx.init(id);
|
|
4895
|
+
void this.f.load(id, this.mode());
|
|
4861
4896
|
});
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
title: 'Counter offer',
|
|
4868
|
-
height: '90%',
|
|
4869
|
-
cancelText: 'Cancel',
|
|
4870
|
-
confirmText: 'Confirm',
|
|
4871
|
-
component: OfferCounterDialog,
|
|
4872
|
-
data: {
|
|
4873
|
-
term: this.offer.rentalTerms,
|
|
4874
|
-
},
|
|
4875
|
-
};
|
|
4876
|
-
this.dialogService.open(options);
|
|
4877
|
-
this.dialogService.confirmed().subscribe((res) => {
|
|
4878
|
-
if (res) {
|
|
4879
|
-
console.log(res);
|
|
4880
|
-
this.propertyOfferCounterService.counterByTenant(this.id, res).subscribe({
|
|
4881
|
-
next: (res) => {
|
|
4882
|
-
this.snackBarService.open('Counter sent');
|
|
4883
|
-
},
|
|
4884
|
-
error: (error) => {
|
|
4885
|
-
this.snackBarService.open(error.message);
|
|
4886
|
-
},
|
|
4887
|
-
});
|
|
4897
|
+
this.route.queryParamMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
|
4898
|
+
const sessionId = params.get('session_id');
|
|
4899
|
+
const offerId = this.currentOfferId;
|
|
4900
|
+
if (!offerId || !sessionId) {
|
|
4901
|
+
return;
|
|
4888
4902
|
}
|
|
4903
|
+
this.propertyOfferService.checkOfferPaymentStatus(offerId, sessionId).subscribe({
|
|
4904
|
+
next: (res) => {
|
|
4905
|
+
if (res.status === 'paid') {
|
|
4906
|
+
void this.ctx.reloadOffer(offerId);
|
|
4907
|
+
void this.ctx.reloadHistory(offerId);
|
|
4908
|
+
void this.f.load(offerId, this.mode());
|
|
4909
|
+
}
|
|
4910
|
+
},
|
|
4911
|
+
});
|
|
4889
4912
|
});
|
|
4890
4913
|
}
|
|
4891
|
-
|
|
4914
|
+
formatMoney(amount) {
|
|
4915
|
+
if (amount === null || amount === undefined) {
|
|
4916
|
+
return '—';
|
|
4917
|
+
}
|
|
4918
|
+
return new Intl.NumberFormat('en-GB', {
|
|
4919
|
+
style: 'currency',
|
|
4920
|
+
currency: 'GBP',
|
|
4921
|
+
maximumFractionDigits: 0,
|
|
4922
|
+
}).format(amount);
|
|
4923
|
+
}
|
|
4924
|
+
humanize(value) {
|
|
4925
|
+
if (!value) {
|
|
4926
|
+
return '—';
|
|
4927
|
+
}
|
|
4928
|
+
return value
|
|
4929
|
+
.trim()
|
|
4930
|
+
.replace(/[_-]+/g, ' ')
|
|
4931
|
+
.toLowerCase()
|
|
4932
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
4933
|
+
}
|
|
4892
4934
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
4893
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade], usesInheritance: true, ngImport: i0, template: "@if (
|
|
4935
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null } }, providers: [OfferDetailContext, PropertyOfferNegotiationFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-offer-detail\">\n @if (mode() === 'agent') {\n <rolatech-property-workspace-shell\n [eyebrow]=\"resolvedEyebrow()\"\n [title]=\"resolvedTitle()\"\n [description]=\"resolvedDescription()\"\n [stats]=\"stats()\"\n >\n <div workspace-actions class=\"property-offer-detail__workspace-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-property-workspace-shell>\n } @else {\n <rolatech-page-collection-shell [eyebrow]=\"resolvedEyebrow()\" [title]=\"resolvedTitle()\" [subtitle]=\"resolvedDescription()\">\n <div page-shell-header-meta class=\"property-offer-detail__header-meta\">\n <div class=\"property-offer-detail__page-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <div class=\"property-offer-detail__summary\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-offer-detail__summary-card\">\n <span class=\"property-offer-detail__summary-value\">{{ stat.value }}</span>\n <span class=\"property-offer-detail__summary-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-offer-detail__summary-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-page-collection-shell>\n }\n\n <ng-template #detailContent>\n @if (ctx.error()) {\n <section class=\"property-offer-detail__error\">{{ ctx.error() }}</section>\n }\n\n @if (ctx.offer()) {\n <div class=\"property-offer-detail__grid\">\n <div class=\"property-offer-detail__main\">\n <rolatech-offer-header-card></rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"property-offer-detail__side\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n } @else {\n <section class=\"property-offer-detail__loading\">Loading offer detail...</section>\n }\n </ng-template>\n</div>\n", styles: [":host{display:block}.property-offer-detail__workspace-actions{display:flex;justify-content:flex-end}.property-offer-detail__header-meta{display:flex;flex-direction:column;gap:.85rem}.property-offer-detail__page-actions{display:flex;justify-content:flex-end}.property-offer-detail__summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:.85rem}.property-offer-detail__summary-card{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-offer-detail__summary-label,.property-offer-detail__summary-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-offer-detail__grid{display:grid;gap:1rem}.property-offer-detail__main,.property-offer-detail__side{display:flex;flex-direction:column;gap:1rem}.property-offer-detail__error,.property-offer-detail__loading{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:var(--rt-base-background, #ffffff);padding:1rem 1.1rem;color:var(--rt-text-secondary)}.property-offer-detail__error{color:#b91c1c;background:color-mix(in srgb,#fff1f2 84%,var(--rt-base-background, #ffffff))}@media(min-width:1024px){.property-offer-detail__summary{grid-template-columns:repeat(4,minmax(0,1fr))}.property-offer-detail__grid{grid-template-columns:minmax(0,1.75fr) minmax(20rem,.9fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: PageCollectionShellComponent, selector: "rolatech-page-collection-shell", inputs: ["eyebrow", "title", "subtitle"] }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }, { kind: "component", type: OfferHeaderCard, selector: "rolatech-offer-header-card" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferSideSummaryCard, selector: "rolatech-offer-side-summary-card" }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }] });
|
|
4894
4936
|
}
|
|
4895
4937
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, decorators: [{
|
|
4896
4938
|
type: Component,
|
|
4897
4939
|
args: [{ selector: 'rolatech-property-offer-detail', imports: [
|
|
4898
4940
|
CommonModule,
|
|
4941
|
+
RouterLink,
|
|
4899
4942
|
MatButtonModule,
|
|
4900
|
-
|
|
4901
|
-
|
|
4943
|
+
PageCollectionShellComponent,
|
|
4944
|
+
PropertyWorkspaceShell,
|
|
4902
4945
|
OfferHeaderCard,
|
|
4903
4946
|
OfferItemCard,
|
|
4904
4947
|
OfferRentalTermsCard,
|
|
@@ -4906,47 +4949,195 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4906
4949
|
OfferTenantsAccordion,
|
|
4907
4950
|
OfferSideSummaryCard,
|
|
4908
4951
|
OfferHistoryAccordion,
|
|
4909
|
-
MatFormFieldModule,
|
|
4910
|
-
MatInputModule,
|
|
4911
|
-
MatSelectModule,
|
|
4912
|
-
MatDatepickerModule,
|
|
4913
|
-
MatNativeDateModule,
|
|
4914
|
-
MatSlideToggleModule,
|
|
4915
|
-
MatChipsModule,
|
|
4916
|
-
FormsModule,
|
|
4917
4952
|
OfferCounterPreviousCard,
|
|
4918
4953
|
OfferCounterLatestCard,
|
|
4919
4954
|
OfferNegotiationSection,
|
|
4920
|
-
], providers: [OfferDetailContext, PropertyOfferNegotiationFacade], template: "@if (
|
|
4921
|
-
}] });
|
|
4955
|
+
], providers: [OfferDetailContext, PropertyOfferNegotiationFacade], template: "<div class=\"property-offer-detail\">\n @if (mode() === 'agent') {\n <rolatech-property-workspace-shell\n [eyebrow]=\"resolvedEyebrow()\"\n [title]=\"resolvedTitle()\"\n [description]=\"resolvedDescription()\"\n [stats]=\"stats()\"\n >\n <div workspace-actions class=\"property-offer-detail__workspace-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-property-workspace-shell>\n } @else {\n <rolatech-page-collection-shell [eyebrow]=\"resolvedEyebrow()\" [title]=\"resolvedTitle()\" [subtitle]=\"resolvedDescription()\">\n <div page-shell-header-meta class=\"property-offer-detail__header-meta\">\n <div class=\"property-offer-detail__page-actions\">\n <a mat-stroked-button routerLink=\"../\">{{ resolvedBackLabel() }}</a>\n </div>\n\n <div class=\"property-offer-detail__summary\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-offer-detail__summary-card\">\n <span class=\"property-offer-detail__summary-value\">{{ stat.value }}</span>\n <span class=\"property-offer-detail__summary-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-offer-detail__summary-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-page-collection-shell>\n }\n\n <ng-template #detailContent>\n @if (ctx.error()) {\n <section class=\"property-offer-detail__error\">{{ ctx.error() }}</section>\n }\n\n @if (ctx.offer()) {\n <div class=\"property-offer-detail__grid\">\n <div class=\"property-offer-detail__main\">\n <rolatech-offer-header-card></rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"property-offer-detail__side\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n } @else {\n <section class=\"property-offer-detail__loading\">Loading offer detail...</section>\n }\n </ng-template>\n</div>\n", styles: [":host{display:block}.property-offer-detail__workspace-actions{display:flex;justify-content:flex-end}.property-offer-detail__header-meta{display:flex;flex-direction:column;gap:.85rem}.property-offer-detail__page-actions{display:flex;justify-content:flex-end}.property-offer-detail__summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:.85rem}.property-offer-detail__summary-card{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-offer-detail__summary-label,.property-offer-detail__summary-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-offer-detail__grid{display:grid;gap:1rem}.property-offer-detail__main,.property-offer-detail__side{display:flex;flex-direction:column;gap:1rem}.property-offer-detail__error,.property-offer-detail__loading{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:var(--rt-base-background, #ffffff);padding:1rem 1.1rem;color:var(--rt-text-secondary)}.property-offer-detail__error{color:#b91c1c;background:color-mix(in srgb,#fff1f2 84%,var(--rt-base-background, #ffffff))}@media(min-width:1024px){.property-offer-detail__summary{grid-template-columns:repeat(4,minmax(0,1fr))}.property-offer-detail__grid{grid-template-columns:minmax(0,1.75fr) minmax(20rem,.9fr);align-items:start}}\n"] }]
|
|
4956
|
+
}], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], backLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "backLabel", required: false }] }] } });
|
|
4922
4957
|
|
|
4923
|
-
class
|
|
4958
|
+
class ViewingDetailContext {
|
|
4924
4959
|
propertyService = inject(PropertyService);
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4960
|
+
viewingId = signal(null, ...(ngDevMode ? [{ debugName: "viewingId" }] : []));
|
|
4961
|
+
viewing = signal(null, ...(ngDevMode ? [{ debugName: "viewing" }] : []));
|
|
4962
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
4963
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
4964
|
+
constructor() {
|
|
4965
|
+
effect(() => {
|
|
4966
|
+
const id = this.viewingId();
|
|
4967
|
+
if (!id) {
|
|
4968
|
+
return;
|
|
4969
|
+
}
|
|
4970
|
+
void this.reload(id);
|
|
4931
4971
|
});
|
|
4932
4972
|
}
|
|
4933
|
-
|
|
4934
|
-
this.
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4973
|
+
init(id) {
|
|
4974
|
+
if (this.viewingId() !== id) {
|
|
4975
|
+
this.viewing.set(null);
|
|
4976
|
+
}
|
|
4977
|
+
this.error.set(null);
|
|
4978
|
+
this.viewingId.set(id);
|
|
4979
|
+
}
|
|
4980
|
+
clear() {
|
|
4981
|
+
this.viewingId.set(null);
|
|
4982
|
+
this.viewing.set(null);
|
|
4983
|
+
this.loading.set(false);
|
|
4984
|
+
this.error.set(null);
|
|
4985
|
+
}
|
|
4986
|
+
updateViewing(updater) {
|
|
4987
|
+
const current = this.viewing();
|
|
4988
|
+
if (!current) {
|
|
4989
|
+
return;
|
|
4990
|
+
}
|
|
4991
|
+
this.viewing.set(updater(current));
|
|
4992
|
+
}
|
|
4993
|
+
async reload(id = this.viewingId()) {
|
|
4994
|
+
if (!id) {
|
|
4995
|
+
return;
|
|
4996
|
+
}
|
|
4997
|
+
this.loading.set(true);
|
|
4998
|
+
this.error.set(null);
|
|
4999
|
+
try {
|
|
5000
|
+
const response = await firstValueFrom(this.propertyService.getViewing(id));
|
|
5001
|
+
if (this.viewingId() !== id) {
|
|
5002
|
+
return;
|
|
5003
|
+
}
|
|
5004
|
+
this.viewing.set(response.data ?? response);
|
|
5005
|
+
}
|
|
5006
|
+
catch (error) {
|
|
5007
|
+
if (this.viewingId() === id) {
|
|
5008
|
+
this.error.set(this.errorMessage(error, 'Failed to load viewing detail.'));
|
|
5009
|
+
}
|
|
5010
|
+
console.error(error);
|
|
5011
|
+
}
|
|
5012
|
+
finally {
|
|
5013
|
+
if (this.viewingId() === id) {
|
|
5014
|
+
this.loading.set(false);
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
errorMessage(error, fallback) {
|
|
5019
|
+
return error?.error?.message ?? error?.message ?? fallback;
|
|
5020
|
+
}
|
|
5021
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5022
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext });
|
|
5023
|
+
}
|
|
5024
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext, decorators: [{
|
|
5025
|
+
type: Injectable
|
|
5026
|
+
}], ctorParameters: () => [] });
|
|
5027
|
+
|
|
5028
|
+
class PropertyViewingFacade {
|
|
5029
|
+
propertyService = inject(PropertyService);
|
|
5030
|
+
snackBarService = inject(SnackBarService);
|
|
5031
|
+
ctx = inject(ViewingDetailContext);
|
|
5032
|
+
viewing = computed(() => this.ctx.viewing(), ...(ngDevMode ? [{ debugName: "viewing" }] : []));
|
|
5033
|
+
acceptingSlotId = signal(null, ...(ngDevMode ? [{ debugName: "acceptingSlotId" }] : []));
|
|
5034
|
+
cancelling = signal(false, ...(ngDevMode ? [{ debugName: "cancelling" }] : []));
|
|
5035
|
+
requiresSlotAcceptance = computed(() => {
|
|
5036
|
+
const viewing = this.viewing();
|
|
5037
|
+
return (!viewing?.viewingDate &&
|
|
5038
|
+
(viewing?.slotDecisionRequired === true ||
|
|
5039
|
+
this.slotProposalSource(viewing) === 'ADMIN_COUNTER' ||
|
|
5040
|
+
propertyViewingStatusCode(viewing?.status) === 'COUNTERED'));
|
|
5041
|
+
}, ...(ngDevMode ? [{ debugName: "requiresSlotAcceptance" }] : []));
|
|
5042
|
+
canCancel = computed(() => {
|
|
5043
|
+
const code = propertyViewingStatusCode(this.viewing()?.status);
|
|
5044
|
+
return code !== 'CANCELLED' && code !== 'COMPLETED';
|
|
5045
|
+
}, ...(ngDevMode ? [{ debugName: "canCancel" }] : []));
|
|
5046
|
+
canAcceptCounterSlot(slot) {
|
|
5047
|
+
return this.requiresSlotAcceptance() && !!slot?.id && this.acceptingSlotId() !== slot.id;
|
|
5048
|
+
}
|
|
5049
|
+
async cancel() {
|
|
5050
|
+
const viewingId = this.ctx.viewingId();
|
|
5051
|
+
if (!viewingId || !this.viewing() || !this.canCancel() || this.cancelling()) {
|
|
5052
|
+
return;
|
|
5053
|
+
}
|
|
5054
|
+
this.cancelling.set(true);
|
|
5055
|
+
try {
|
|
5056
|
+
await firstValueFrom(this.propertyService.cancelViewing(viewingId));
|
|
5057
|
+
this.ctx.updateViewing((viewing) => ({
|
|
5058
|
+
...viewing,
|
|
5059
|
+
status: 'CANCELLED',
|
|
5060
|
+
slotDecisionRequired: false,
|
|
5061
|
+
}));
|
|
5062
|
+
this.snackBarService.open('Cancelled');
|
|
5063
|
+
}
|
|
5064
|
+
catch (error) {
|
|
5065
|
+
this.snackBarService.open(this.errorMessage(error, 'Failed to cancel viewing.'));
|
|
5066
|
+
}
|
|
5067
|
+
finally {
|
|
5068
|
+
this.cancelling.set(false);
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
async acceptCounterSlot(slot) {
|
|
5072
|
+
const viewingId = this.ctx.viewingId();
|
|
5073
|
+
if (!viewingId) {
|
|
5074
|
+
return;
|
|
5075
|
+
}
|
|
5076
|
+
if (!slot?.id) {
|
|
5077
|
+
this.snackBarService.open('This slot cannot be accepted because the slot id is missing.');
|
|
5078
|
+
return;
|
|
5079
|
+
}
|
|
5080
|
+
this.acceptingSlotId.set(slot.id);
|
|
5081
|
+
try {
|
|
5082
|
+
const response = await firstValueFrom(this.propertyService.confirmViewing(viewingId, slot.id));
|
|
5083
|
+
const nextViewing = response?.data;
|
|
5084
|
+
this.ctx.updateViewing((viewing) => ({
|
|
5085
|
+
...viewing,
|
|
5086
|
+
...nextViewing,
|
|
5087
|
+
status: nextViewing?.status ?? 'APPROVED',
|
|
5088
|
+
viewingDate: nextViewing?.viewingDate ?? slot.date ?? viewing.viewingDate,
|
|
5089
|
+
viewingTime: nextViewing?.viewingTime ?? slot.time ?? viewing.viewingTime,
|
|
5090
|
+
proposedSlots: nextViewing?.proposedSlots ?? viewing.proposedSlots,
|
|
5091
|
+
item: nextViewing?.item ?? viewing.item,
|
|
5092
|
+
slotProposalSource: nextViewing?.slotProposalSource ?? viewing.slotProposalSource,
|
|
5093
|
+
slotDecisionRequired: nextViewing?.slotDecisionRequired ?? false,
|
|
5094
|
+
}));
|
|
5095
|
+
this.snackBarService.open('Viewing confirmed.');
|
|
5096
|
+
}
|
|
5097
|
+
catch (error) {
|
|
5098
|
+
this.snackBarService.open(this.errorMessage(error, 'Failed to confirm viewing.'));
|
|
5099
|
+
}
|
|
5100
|
+
finally {
|
|
5101
|
+
this.acceptingSlotId.set(null);
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
slotProposalSource(viewing) {
|
|
5105
|
+
const explicitSource = viewing?.slotProposalSource;
|
|
5106
|
+
if (explicitSource) {
|
|
5107
|
+
return explicitSource.toUpperCase();
|
|
5108
|
+
}
|
|
5109
|
+
const slotSource = viewing?.proposedSlots?.[0]?.source;
|
|
5110
|
+
return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
|
|
5111
|
+
}
|
|
5112
|
+
errorMessage(error, fallback) {
|
|
5113
|
+
return error?.error?.message ?? error?.message ?? fallback;
|
|
5114
|
+
}
|
|
5115
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5116
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade });
|
|
5117
|
+
}
|
|
5118
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade, decorators: [{
|
|
5119
|
+
type: Injectable
|
|
5120
|
+
}] });
|
|
5121
|
+
|
|
5122
|
+
class PropertyViewingDetailComponent extends BaseComponent {
|
|
5123
|
+
ctx = inject(ViewingDetailContext);
|
|
5124
|
+
facade = inject(PropertyViewingFacade);
|
|
5125
|
+
viewing = computed(() => this.ctx.viewing(), ...(ngDevMode ? [{ debugName: "viewing" }] : []));
|
|
5126
|
+
applicantType = PropertyApplicantType;
|
|
5127
|
+
constructor() {
|
|
5128
|
+
super();
|
|
5129
|
+
effect(() => {
|
|
5130
|
+
this.titleService.setTitle(this.viewing()?.item?.title || 'Viewing detail');
|
|
4939
5131
|
});
|
|
4940
5132
|
}
|
|
4941
|
-
|
|
4942
|
-
this.
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
this.
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
},
|
|
5133
|
+
ngOnInit() {
|
|
5134
|
+
this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
|
5135
|
+
const id = params.get('id');
|
|
5136
|
+
if (!id) {
|
|
5137
|
+
this.ctx.clear();
|
|
5138
|
+
return;
|
|
5139
|
+
}
|
|
5140
|
+
this.ctx.init(id);
|
|
4950
5141
|
});
|
|
4951
5142
|
}
|
|
4952
5143
|
statusLabel(status) {
|
|
@@ -4965,7 +5156,12 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
4965
5156
|
}
|
|
4966
5157
|
}
|
|
4967
5158
|
statusHeadline() {
|
|
4968
|
-
|
|
5159
|
+
if (this.facade.requiresSlotAcceptance()) {
|
|
5160
|
+
return 'The listing team returned new slots for you to choose from.';
|
|
5161
|
+
}
|
|
5162
|
+
switch (propertyViewingStatusCode(this.viewing()?.status)) {
|
|
5163
|
+
case 'COUNTERED':
|
|
5164
|
+
return 'The listing team returned new slots for you to choose from.';
|
|
4969
5165
|
case 'APPROVED':
|
|
4970
5166
|
return 'Your viewing is confirmed.';
|
|
4971
5167
|
case 'COMPLETED':
|
|
@@ -4979,13 +5175,17 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
4979
5175
|
}
|
|
4980
5176
|
}
|
|
4981
5177
|
statusDescription() {
|
|
4982
|
-
|
|
5178
|
+
const viewing = this.viewing();
|
|
5179
|
+
if (!viewing) {
|
|
4983
5180
|
return '';
|
|
4984
5181
|
}
|
|
5182
|
+
if (this.facade.requiresSlotAcceptance()) {
|
|
5183
|
+
return 'Review the three returned options below and accept the slot that works best for you.';
|
|
5184
|
+
}
|
|
4985
5185
|
if (this.confirmedSlotLabel()) {
|
|
4986
5186
|
return `Confirmed for ${this.confirmedSlotLabel()}.`;
|
|
4987
5187
|
}
|
|
4988
|
-
switch (propertyViewingStatusCode(
|
|
5188
|
+
switch (propertyViewingStatusCode(viewing.status)) {
|
|
4989
5189
|
case 'REJECTED':
|
|
4990
5190
|
return 'Review the proposed slots and submit a new request if you still want to visit the property.';
|
|
4991
5191
|
case 'CANCELLED':
|
|
@@ -4997,34 +5197,38 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
4997
5197
|
}
|
|
4998
5198
|
}
|
|
4999
5199
|
confirmedSlotLabel() {
|
|
5000
|
-
|
|
5200
|
+
const viewing = this.viewing();
|
|
5201
|
+
if (!viewing?.viewingDate) {
|
|
5001
5202
|
return null;
|
|
5002
5203
|
}
|
|
5003
|
-
const date = new Date(
|
|
5204
|
+
const date = new Date(viewing.viewingDate);
|
|
5004
5205
|
const hasValidDate = !Number.isNaN(date.getTime());
|
|
5005
5206
|
const formattedDate = hasValidDate
|
|
5006
5207
|
? new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium' }).format(date)
|
|
5007
|
-
:
|
|
5008
|
-
return
|
|
5208
|
+
: viewing.viewingDate;
|
|
5209
|
+
return viewing.viewingTime ? `${formattedDate} at ${viewing.viewingTime}` : formattedDate;
|
|
5009
5210
|
}
|
|
5010
5211
|
isConfirmedSlot(slot) {
|
|
5011
|
-
|
|
5212
|
+
const viewing = this.viewing();
|
|
5213
|
+
return !!viewing?.viewingDate && slot.date === viewing.viewingDate && slot.time === viewing.viewingTime;
|
|
5012
5214
|
}
|
|
5013
5215
|
primaryMediaUrl() {
|
|
5014
|
-
return this.viewing?.item?.media?.[0]?.url || '';
|
|
5216
|
+
return this.viewing()?.item?.media?.[0]?.url || '';
|
|
5015
5217
|
}
|
|
5016
5218
|
requesterName() {
|
|
5017
|
-
const
|
|
5219
|
+
const viewing = this.viewing();
|
|
5220
|
+
const fullName = `${viewing?.firstName ?? ''} ${viewing?.lastName ?? ''}`.trim();
|
|
5018
5221
|
return fullName || 'Viewer';
|
|
5019
5222
|
}
|
|
5020
5223
|
contactSummary() {
|
|
5021
|
-
|
|
5224
|
+
const viewing = this.viewing();
|
|
5225
|
+
if (!viewing) {
|
|
5022
5226
|
return 'Contact details available below';
|
|
5023
5227
|
}
|
|
5024
|
-
if (
|
|
5025
|
-
return `${
|
|
5228
|
+
if (viewing.email && viewing.phone) {
|
|
5229
|
+
return `${viewing.email} · ${viewing.phone}`;
|
|
5026
5230
|
}
|
|
5027
|
-
return
|
|
5231
|
+
return viewing.email || viewing.phone || 'Contact details available below';
|
|
5028
5232
|
}
|
|
5029
5233
|
viewerCategoryLabel(value) {
|
|
5030
5234
|
if (!value) {
|
|
@@ -5046,10 +5250,6 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
5046
5250
|
requesterTypeLabel(value) {
|
|
5047
5251
|
return value ? this.humanize(value) : 'Direct request';
|
|
5048
5252
|
}
|
|
5049
|
-
canCancel() {
|
|
5050
|
-
const code = propertyViewingStatusCode(this.viewing?.status);
|
|
5051
|
-
return code !== 'CANCELLED' && code !== 'COMPLETED';
|
|
5052
|
-
}
|
|
5053
5253
|
humanize(value) {
|
|
5054
5254
|
return value
|
|
5055
5255
|
.trim()
|
|
@@ -5057,8 +5257,8 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
5057
5257
|
.toLowerCase()
|
|
5058
5258
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
5059
5259
|
}
|
|
5060
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps:
|
|
5061
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Availability shared with the listing team</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\" i18n>\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
5260
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5261
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", providers: [ViewingDetailContext, PropertyViewingFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing() ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing() ? statusLabel(viewing()?.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing()?.item?.propertyId; as propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', propertyId]\" i18n>Open property</a>\n }\n @if (viewing() && facade.canCancel()) {\n <button mat-flat-button type=\"button\" [disabled]=\"facade.cancelling()\" (click)=\"facade.cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing(); as viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (facade.requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (facade.requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!facade.canAcceptCounterSlot(slot)\"\n (click)=\"facade.acceptCounterSlot(slot)\"\n >\n @if (facade.acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (facade.requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
5062
5262
|
}
|
|
5063
5263
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, decorators: [{
|
|
5064
5264
|
type: Component,
|
|
@@ -5072,8 +5272,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
5072
5272
|
ToolbarComponent,
|
|
5073
5273
|
RouterLink,
|
|
5074
5274
|
PricePipe,
|
|
5075
|
-
], template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Availability shared with the listing team</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\" i18n>\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"] }]
|
|
5076
|
-
}] });
|
|
5275
|
+
], providers: [ViewingDetailContext, PropertyViewingFacade], template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing() ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing() ? statusLabel(viewing()?.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing()?.item?.propertyId; as propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', propertyId]\" i18n>Open property</a>\n }\n @if (viewing() && facade.canCancel()) {\n <button mat-flat-button type=\"button\" [disabled]=\"facade.cancelling()\" (click)=\"facade.cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing(); as viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (facade.requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (facade.requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!facade.canAcceptCounterSlot(slot)\"\n (click)=\"facade.acceptCounterSlot(slot)\"\n >\n @if (facade.acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (facade.requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"] }]
|
|
5276
|
+
}], ctorParameters: () => [] });
|
|
5077
5277
|
|
|
5078
5278
|
class PropertyWishlistComponent extends BaseComponent {
|
|
5079
5279
|
propertyService = inject(PropertyService);
|
|
@@ -6984,7 +7184,7 @@ class PropertyOfferCreate {
|
|
|
6984
7184
|
}
|
|
6985
7185
|
}
|
|
6986
7186
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6987
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? '
|
|
7187
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Market workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "component", type: PropertyIntentShell, selector: "rolatech-property-intent-shell", inputs: ["property"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }] });
|
|
6988
7188
|
}
|
|
6989
7189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, decorators: [{
|
|
6990
7190
|
type: Component,
|
|
@@ -6995,7 +7195,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
6995
7195
|
PropertyOfferSaleForm,
|
|
6996
7196
|
PropertyIntentShell,
|
|
6997
7197
|
PropertyMarketBreadcrumbComponent,
|
|
6998
|
-
], providers: [OfferFormFacade], template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? '
|
|
7198
|
+
], providers: [OfferFormFacade], template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Market workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"] }]
|
|
6999
7199
|
}], ctorParameters: () => [] });
|
|
7000
7200
|
|
|
7001
7201
|
const propertyRoutes = [
|
|
@@ -7005,7 +7205,7 @@ const propertyRoutes = [
|
|
|
7005
7205
|
children: [
|
|
7006
7206
|
{
|
|
7007
7207
|
path: '',
|
|
7008
|
-
loadComponent: () => import('./rolatech-angular-property-property-index.component-
|
|
7208
|
+
loadComponent: () => import('./rolatech-angular-property-property-index.component-CLCJ78vg.mjs').then((x) => x.PropertyIndexComponent),
|
|
7009
7209
|
},
|
|
7010
7210
|
],
|
|
7011
7211
|
},
|
|
@@ -8860,6 +9060,9 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
|
|
|
8860
9060
|
propertyService = inject(PropertyService);
|
|
8861
9061
|
authUserService = inject(AuthUserService);
|
|
8862
9062
|
applicantType = PropertyApplicantType;
|
|
9063
|
+
counterMode = signal(false, ...(ngDevMode ? [{ debugName: "counterMode" }] : []));
|
|
9064
|
+
countering = signal(false, ...(ngDevMode ? [{ debugName: "countering" }] : []));
|
|
9065
|
+
counterSlots = signal(this.createCounterSlots(), ...(ngDevMode ? [{ debugName: "counterSlots" }] : []));
|
|
8863
9066
|
viewing;
|
|
8864
9067
|
agent;
|
|
8865
9068
|
ngOnInit() {
|
|
@@ -8870,6 +9073,7 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
|
|
|
8870
9073
|
next: (res) => {
|
|
8871
9074
|
this.viewing = res.data;
|
|
8872
9075
|
this.titleService.setTitle(this.viewing.item?.title || 'Manage viewing request');
|
|
9076
|
+
this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
|
|
8873
9077
|
if (this.viewing.agentId) {
|
|
8874
9078
|
this.getAgentPublicInfo(this.viewing.agentId);
|
|
8875
9079
|
}
|
|
@@ -8914,6 +9118,8 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
|
|
|
8914
9118
|
proposedSlots: res?.data?.proposedSlots ?? this.viewing.proposedSlots,
|
|
8915
9119
|
item: res?.data?.item ?? this.viewing.item,
|
|
8916
9120
|
};
|
|
9121
|
+
this.counterMode.set(false);
|
|
9122
|
+
this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
|
|
8917
9123
|
this.snackBarService.open('Viewing confirmed');
|
|
8918
9124
|
},
|
|
8919
9125
|
error: (error) => {
|
|
@@ -8939,7 +9145,12 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
|
|
|
8939
9145
|
}
|
|
8940
9146
|
}
|
|
8941
9147
|
statusHeadline() {
|
|
9148
|
+
if (this.isAwaitingAcceptance()) {
|
|
9149
|
+
return 'Counter slots sent. Waiting for the requester to pick one.';
|
|
9150
|
+
}
|
|
8942
9151
|
switch (propertyViewingStatusCode(this.viewing?.status)) {
|
|
9152
|
+
case 'COUNTERED':
|
|
9153
|
+
return 'Counter slots sent. Waiting for the requester to pick one.';
|
|
8943
9154
|
case 'APPROVED':
|
|
8944
9155
|
return 'Viewing confirmed with the applicant.';
|
|
8945
9156
|
case 'COMPLETED':
|
|
@@ -8956,6 +9167,9 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
|
|
|
8956
9167
|
if (!this.viewing) {
|
|
8957
9168
|
return '';
|
|
8958
9169
|
}
|
|
9170
|
+
if (this.isAwaitingAcceptance()) {
|
|
9171
|
+
return 'The original requested times did not fit the diary, so three new options have been returned for the requester to accept.';
|
|
9172
|
+
}
|
|
8959
9173
|
if (this.confirmedSlotLabel()) {
|
|
8960
9174
|
return `The selected slot is ${this.confirmedSlotLabel()} and should now be visible across the viewing workflow.`;
|
|
8961
9175
|
}
|
|
@@ -8984,7 +9198,81 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
|
|
|
8984
9198
|
return !!this.viewing?.viewingDate && slot.date === this.viewing.viewingDate && slot.time === this.viewing.viewingTime;
|
|
8985
9199
|
}
|
|
8986
9200
|
canConfirmSlot(slot) {
|
|
8987
|
-
return !this.viewing?.viewingDate && !!slot?.id;
|
|
9201
|
+
return !this.viewing?.viewingDate && !!slot?.id && !this.isAwaitingAcceptance();
|
|
9202
|
+
}
|
|
9203
|
+
canCounterSlots() {
|
|
9204
|
+
const code = propertyViewingStatusCode(this.viewing?.status);
|
|
9205
|
+
return !this.viewing?.viewingDate && code !== 'COMPLETED' && code !== 'CANCELLED' && code !== 'REJECTED';
|
|
9206
|
+
}
|
|
9207
|
+
isAwaitingAcceptance() {
|
|
9208
|
+
return (!this.viewing?.viewingDate &&
|
|
9209
|
+
(this.viewing?.slotDecisionRequired === true ||
|
|
9210
|
+
this.slotProposalSource() === 'ADMIN_COUNTER' ||
|
|
9211
|
+
propertyViewingStatusCode(this.viewing?.status) === 'COUNTERED'));
|
|
9212
|
+
}
|
|
9213
|
+
openCounterSlots() {
|
|
9214
|
+
this.counterSlots.set(this.createCounterSlots(this.viewing?.proposedSlots));
|
|
9215
|
+
this.counterMode.set(true);
|
|
9216
|
+
}
|
|
9217
|
+
cancelCounterSlots() {
|
|
9218
|
+
this.counterMode.set(false);
|
|
9219
|
+
this.counterSlots.set(this.createCounterSlots(this.viewing?.proposedSlots));
|
|
9220
|
+
}
|
|
9221
|
+
updateCounterSlot(index, slot) {
|
|
9222
|
+
this.counterSlots.update((current) => current.map((item, itemIndex) => itemIndex === index
|
|
9223
|
+
? {
|
|
9224
|
+
...item,
|
|
9225
|
+
...slot,
|
|
9226
|
+
}
|
|
9227
|
+
: item));
|
|
9228
|
+
}
|
|
9229
|
+
sendCounterSlots() {
|
|
9230
|
+
const counterSlots = this.counterSlots()
|
|
9231
|
+
.map((slot) => ({
|
|
9232
|
+
date: slot.date?.trim?.() ?? slot.date,
|
|
9233
|
+
time: slot.time?.trim?.() ?? slot.time,
|
|
9234
|
+
}))
|
|
9235
|
+
.filter((slot) => slot.date && slot.time);
|
|
9236
|
+
if (counterSlots.length !== 3) {
|
|
9237
|
+
this.snackBarService.open('Provide three counter slots before sending them to the requester.');
|
|
9238
|
+
return;
|
|
9239
|
+
}
|
|
9240
|
+
const uniqueSlots = new Set(counterSlots.map((slot) => `${slot.date}::${slot.time}`));
|
|
9241
|
+
if (uniqueSlots.size !== counterSlots.length) {
|
|
9242
|
+
this.snackBarService.open('Each counter slot must use a different date and time.');
|
|
9243
|
+
return;
|
|
9244
|
+
}
|
|
9245
|
+
this.countering.set(true);
|
|
9246
|
+
this.propertyService.counterViewing(this.id, { counterSlots }).subscribe({
|
|
9247
|
+
next: (res) => {
|
|
9248
|
+
const nextViewing = res?.data;
|
|
9249
|
+
this.viewing = {
|
|
9250
|
+
...this.viewing,
|
|
9251
|
+
...nextViewing,
|
|
9252
|
+
status: nextViewing?.status ?? 'COUNTERED',
|
|
9253
|
+
slotProposalSource: nextViewing?.slotProposalSource ?? 'ADMIN_COUNTER',
|
|
9254
|
+
slotDecisionRequired: nextViewing?.slotDecisionRequired ?? true,
|
|
9255
|
+
counteredAt: nextViewing?.counteredAt ?? new Date().toISOString(),
|
|
9256
|
+
proposedSlots: nextViewing?.proposedSlots ??
|
|
9257
|
+
counterSlots.map((slot, index) => ({
|
|
9258
|
+
...slot,
|
|
9259
|
+
id: this.viewing.proposedSlots[index]?.id,
|
|
9260
|
+
source: 'ADMIN_COUNTER',
|
|
9261
|
+
})),
|
|
9262
|
+
item: nextViewing?.item ?? this.viewing.item,
|
|
9263
|
+
};
|
|
9264
|
+
this.counterMode.set(false);
|
|
9265
|
+
this.counterSlots.set(this.createCounterSlots(this.viewing.proposedSlots));
|
|
9266
|
+
this.snackBarService.open('Counter slots sent to the requester.');
|
|
9267
|
+
},
|
|
9268
|
+
error: (error) => {
|
|
9269
|
+
this.countering.set(false);
|
|
9270
|
+
this.snackBarService.open(error.message);
|
|
9271
|
+
},
|
|
9272
|
+
complete: () => {
|
|
9273
|
+
this.countering.set(false);
|
|
9274
|
+
},
|
|
9275
|
+
});
|
|
8988
9276
|
}
|
|
8989
9277
|
primaryMediaUrl() {
|
|
8990
9278
|
return this.viewing?.item?.media?.[0]?.url || '';
|
|
@@ -9041,8 +9329,24 @@ class PropertyManageViewingsDetailComponent extends BaseComponent {
|
|
|
9041
9329
|
.toLowerCase()
|
|
9042
9330
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
9043
9331
|
}
|
|
9332
|
+
slotProposalSource() {
|
|
9333
|
+
const explicitSource = this.viewing?.slotProposalSource;
|
|
9334
|
+
if (explicitSource) {
|
|
9335
|
+
return explicitSource.toUpperCase();
|
|
9336
|
+
}
|
|
9337
|
+
const slotSource = this.viewing?.proposedSlots?.[0]?.source;
|
|
9338
|
+
return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
|
|
9339
|
+
}
|
|
9340
|
+
createCounterSlots(source = []) {
|
|
9341
|
+
return Array.from({ length: 3 }, (_item, index) => ({
|
|
9342
|
+
date: source[index]?.date ?? '',
|
|
9343
|
+
time: source[index]?.time ?? '',
|
|
9344
|
+
source: source[index]?.source ?? null,
|
|
9345
|
+
state: source[index]?.state ?? null,
|
|
9346
|
+
}));
|
|
9347
|
+
}
|
|
9044
9348
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyManageViewingsDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9045
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyManageViewingsDetailComponent, isStandalone: true, selector: "rolatech-property-manage-viewings-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Choose the slot to confirm</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\" i18n>\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
9349
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyManageViewingsDetailComponent, isStandalone: true, selector: "rolatech-property-manage-viewings-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\">\n @if (isAwaitingAcceptance()) {\n Waiting for requester acceptance\n } @else {\n Choose the slot to confirm\n }\n </h2>\n </div>\n\n @if (canCounterSlots()) {\n <button mat-stroked-button type=\"button\" (click)=\"openCounterSlots()\" i18n>Counter with 3 new slots</button>\n }\n </div>\n\n @if (counterMode()) {\n <div class=\"property-manage-viewings-detail__counter-panel\">\n <div class=\"property-manage-viewings-detail__counter-copy\">\n <strong i18n>Return three new slots</strong>\n <p i18n>\n Use this when the original request does not fit the diary. The requester will need to accept one of these\n returned options.\n </p>\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-grid\">\n @for (slot of counterSlots(); track $index) {\n <div class=\"property-manage-viewings-detail__counter-card\">\n <div class=\"property-manage-viewings-detail__counter-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time\n [proposedTime]=\"slot\"\n (output)=\"updateCounterSlot($index, $event)\"\n ></rolatech-property-viewing-time>\n </div>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"cancelCounterSlots()\" i18n>Cancel</button>\n <button mat-flat-button type=\"button\" [disabled]=\"countering()\" (click)=\"sendCounterSlots()\">\n @if (countering()) {\n Sending...\n } @else {\n Send counter slots\n }\n </button>\n </div>\n </div>\n }\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else if (isAwaitingAcceptance()) {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Sent for acceptance</span>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\">\n @if (isAwaitingAcceptance()) {\n The requester now needs to accept one of the returned slots. Once they do, the accepted time becomes the source\n of truth for this viewing.\n } @else {\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n }\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__counter-panel{display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem;padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-brand-color) 6%,var(--rt-raised-background, #ffffff) 94%)}.property-manage-viewings-detail__counter-copy{display:flex;flex-direction:column;gap:.35rem}.property-manage-viewings-detail__counter-copy p{margin:0;color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__counter-grid{display:grid;gap:.85rem}.property-manage-viewings-detail__counter-card{padding:.95rem 1rem .35rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-manage-viewings-detail__counter-label{margin-bottom:.5rem;color:var(--rt-text-secondary);font-size:.82rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.property-manage-viewings-detail__counter-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.75rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__counter-actions{justify-content:stretch}.property-manage-viewings-detail__counter-actions>*{flex:1 1 100%}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
9046
9350
|
}
|
|
9047
9351
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyManageViewingsDetailComponent, decorators: [{
|
|
9048
9352
|
type: Component,
|
|
@@ -9055,13 +9359,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9055
9359
|
Skeleton,
|
|
9056
9360
|
ToolbarComponent,
|
|
9057
9361
|
PricePipe,
|
|
9058
|
-
], template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Choose the slot to confirm</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\" i18n>\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"] }]
|
|
9362
|
+
FormsModule,
|
|
9363
|
+
PropertyViewingTimeComponent,
|
|
9364
|
+
], template: "<div class=\"property-manage-viewings-detail\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Manage viewing request' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n ></rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-manage-viewings-detail__hero\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <span class=\"property-manage-viewings-detail__eyebrow\" i18n>Manage viewing</span>\n <div class=\"property-manage-viewings-detail__hero-heading\">\n <h1 class=\"property-manage-viewings-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-manage-viewings-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-manage-viewings-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-manage-viewings-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-manage-viewings-detail__fact-grid\">\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting selection' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Applicant type</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__fact\">\n <span class=\"property-manage-viewings-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-manage-viewings-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <div class=\"property-manage-viewings-detail__property-card\">\n <div class=\"property-manage-viewings-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-manage-viewings-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__property-copy\">\n <div class=\"property-manage-viewings-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-manage-viewings-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__summary-card\">\n <span class=\"property-manage-viewings-detail__summary-label\" i18n>Viewer</span>\n <strong class=\"property-manage-viewings-detail__summary-value\">{{ viewerName() }}</strong>\n <span class=\"property-manage-viewings-detail__summary-contact\">{{ viewerContactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-manage-viewings-detail__content\">\n <main class=\"property-manage-viewings-detail__main\">\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Applicant details and affordability</h2>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__info-grid\">\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Name</span>\n <strong>{{ viewerName() }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Move-in target</span>\n <strong>{{ viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared' }}</strong>\n </article>\n <article class=\"property-manage-viewings-detail__info-card\">\n <span class=\"property-manage-viewings-detail__info-label\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-manage-viewings-detail__panel\">\n <div class=\"property-manage-viewings-detail__panel-head\">\n <div>\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\">\n @if (isAwaitingAcceptance()) {\n Waiting for requester acceptance\n } @else {\n Choose the slot to confirm\n }\n </h2>\n </div>\n\n @if (canCounterSlots()) {\n <button mat-stroked-button type=\"button\" (click)=\"openCounterSlots()\" i18n>Counter with 3 new slots</button>\n }\n </div>\n\n @if (counterMode()) {\n <div class=\"property-manage-viewings-detail__counter-panel\">\n <div class=\"property-manage-viewings-detail__counter-copy\">\n <strong i18n>Return three new slots</strong>\n <p i18n>\n Use this when the original request does not fit the diary. The requester will need to accept one of these\n returned options.\n </p>\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-grid\">\n @for (slot of counterSlots(); track $index) {\n <div class=\"property-manage-viewings-detail__counter-card\">\n <div class=\"property-manage-viewings-detail__counter-label\">Option {{ $index + 1 }}</div>\n <rolatech-property-viewing-time\n [proposedTime]=\"slot\"\n (output)=\"updateCounterSlot($index, $event)\"\n ></rolatech-property-viewing-time>\n </div>\n }\n </div>\n\n <div class=\"property-manage-viewings-detail__counter-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"cancelCounterSlots()\" i18n>Cancel</button>\n <button mat-flat-button type=\"button\" [disabled]=\"countering()\" (click)=\"sendCounterSlots()\">\n @if (countering()) {\n Sending...\n } @else {\n Send counter slots\n }\n </button>\n </div>\n </div>\n }\n\n <div class=\"property-manage-viewings-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-manage-viewings-detail__slot-card\"\n [class.property-manage-viewings-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-manage-viewings-detail__slot-copy\">\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-manage-viewings-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__slot-actions\">\n @if (isConfirmedSlot(slot)) {\n <span\n class=\"property-manage-viewings-detail__slot-state property-manage-viewings-detail__slot-state--confirmed\"\n i18n\n >\n Confirmed\n </span>\n } @else if (canConfirmSlot(slot)) {\n <button mat-flat-button type=\"button\" (click)=\"confirmViewingTime(slot)\" i18n>Confirm slot</button>\n } @else if (isAwaitingAcceptance()) {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Sent for acceptance</span>\n } @else {\n <span class=\"property-manage-viewings-detail__slot-state\" i18n>Proposed</span>\n }\n </div>\n </article>\n } @empty {\n <div class=\"property-manage-viewings-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-manage-viewings-detail__side\">\n <div class=\"property-manage-viewings-detail__sticky\">\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Where this enquiry came from</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>User id</span>\n <strong>{{ viewing.userId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n @if (hasAgentCard()) {\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Viewing agent</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Agency contact linked to this request</h2>\n\n <div class=\"property-manage-viewings-detail__summary-list\">\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Name</span>\n <strong>{{ agentName() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-manage-viewings-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n </div>\n </section>\n }\n\n <section class=\"property-manage-viewings-detail__panel property-manage-viewings-detail__panel--subtle\">\n <span class=\"property-manage-viewings-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-manage-viewings-detail__panel-title\" i18n>Move the request forward</h2>\n <p class=\"property-manage-viewings-detail__summary-note\">\n @if (isAwaitingAcceptance()) {\n The requester now needs to accept one of the returned slots. Once they do, the accepted time becomes the source\n of truth for this viewing.\n } @else {\n Confirm one of the proposed slots to turn this request into a scheduled appointment. Once confirmed, the selected\n time becomes the source of truth for this viewing.\n }\n </p>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-manage-viewings-detail__hero property-manage-viewings-detail__hero--loading\">\n <div class=\"property-manage-viewings-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-32\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-manage-viewings-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-manage-viewings-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n\n <div class=\"property-manage-viewings-detail__hero-side\">\n <article class=\"property-manage-viewings-detail__property-card property-manage-viewings-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-manage-viewings-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-manage-viewings-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-manage-viewings-detail__hero--loading{grid-template-columns:1fr}.property-manage-viewings-detail__hero-copy,.property-manage-viewings-detail__hero-side,.property-manage-viewings-detail__main,.property-manage-viewings-detail__side,.property-manage-viewings-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-manage-viewings-detail__eyebrow,.property-manage-viewings-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-manage-viewings-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-manage-viewings-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-manage-viewings-detail__address,.property-manage-viewings-detail__intro,.property-manage-viewings-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-manage-viewings-detail__address{font-size:1rem;font-weight:600}.property-manage-viewings-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-manage-viewings-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-manage-viewings-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-manage-viewings-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-manage-viewings-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__fact,.property-manage-viewings-detail__summary-card,.property-manage-viewings-detail__info-card,.property-manage-viewings-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-manage-viewings-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-manage-viewings-detail__fact-label,.property-manage-viewings-detail__summary-label,.property-manage-viewings-detail__info-label,.property-manage-viewings-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-manage-viewings-detail__fact-value,.property-manage-viewings-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-manage-viewings-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__property-card--loading{min-height:18rem}.property-manage-viewings-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-manage-viewings-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-manage-viewings-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-manage-viewings-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-manage-viewings-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-manage-viewings-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-manage-viewings-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-manage-viewings-detail__summary-card>*{min-width:0}.property-manage-viewings-detail__summary-value,.property-manage-viewings-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-manage-viewings-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__content{display:grid;gap:1rem}.property-manage-viewings-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-manage-viewings-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-manage-viewings-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-manage-viewings-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-manage-viewings-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-manage-viewings-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-manage-viewings-detail__info-card strong,.property-manage-viewings-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-manage-viewings-detail__slot-list,.property-manage-viewings-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-manage-viewings-detail__counter-panel{display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem;padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-brand-color) 6%,var(--rt-raised-background, #ffffff) 94%)}.property-manage-viewings-detail__counter-copy{display:flex;flex-direction:column;gap:.35rem}.property-manage-viewings-detail__counter-copy p{margin:0;color:var(--rt-text-secondary);line-height:1.6}.property-manage-viewings-detail__counter-grid{display:grid;gap:.85rem}.property-manage-viewings-detail__counter-card{padding:.95rem 1rem .35rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-manage-viewings-detail__counter-label{margin-bottom:.5rem;color:var(--rt-text-secondary);font-size:.82rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em}.property-manage-viewings-detail__counter-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.75rem}.property-manage-viewings-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-manage-viewings-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-manage-viewings-detail__slot-copy,.property-manage-viewings-detail__slot-actions{display:flex;flex-direction:column;gap:.45rem}.property-manage-viewings-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-manage-viewings-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-manage-viewings-detail__slot-actions{align-items:flex-end;justify-content:center}.property-manage-viewings-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-manage-viewings-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-manage-viewings-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-manage-viewings-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-manage-viewings-detail{padding:1.25rem}.property-manage-viewings-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-manage-viewings-detail__hero-heading,.property-manage-viewings-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-manage-viewings-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-manage-viewings-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-manage-viewings-detail__fact-grid,.property-manage-viewings-detail__info-grid{grid-template-columns:1fr}.property-manage-viewings-detail__counter-actions{justify-content:stretch}.property-manage-viewings-detail__counter-actions>*{flex:1 1 100%}.property-manage-viewings-detail__slot-card{flex-direction:column}.property-manage-viewings-detail__slot-actions{align-items:flex-start}}\n"] }]
|
|
9059
9365
|
}] });
|
|
9060
9366
|
|
|
9061
9367
|
const propertyManageViewingsRoutes = [
|
|
9062
9368
|
{
|
|
9063
9369
|
path: '',
|
|
9064
|
-
loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-
|
|
9370
|
+
loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-Dx9AXFNI.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
|
|
9065
9371
|
},
|
|
9066
9372
|
{
|
|
9067
9373
|
path: ':id',
|
|
@@ -9110,19 +9416,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9110
9416
|
args: ['class.rolatech-property-market-item']
|
|
9111
9417
|
}], letting: [{ type: i0.Input, args: [{ isSignal: true, alias: "letting", required: true }] }], thumbnail: [{ type: i0.Input, args: [{ isSignal: true, alias: "thumbnail", required: false }] }], list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: false }] }] } });
|
|
9112
9418
|
|
|
9113
|
-
class PropertyWorkspaceShell {
|
|
9114
|
-
eyebrow = input('Workspace', ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
|
|
9115
|
-
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
9116
|
-
description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
|
|
9117
|
-
stats = input([], ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
9118
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9119
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyWorkspaceShell, isStandalone: true, selector: "rolatech-property-workspace-shell", inputs: { eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, stats: { classPropertyName: "stats", publicName: "stats", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
9120
|
-
}
|
|
9121
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, decorators: [{
|
|
9122
|
-
type: Component,
|
|
9123
|
-
args: [{ selector: 'rolatech-property-workspace-shell', imports: [CommonModule], template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"] }]
|
|
9124
|
-
}], propDecorators: { eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], stats: [{ type: i0.Input, args: [{ isSignal: true, alias: "stats", required: false }] }] } });
|
|
9125
|
-
|
|
9126
9419
|
class PropertyMarketIndex extends BaseComponent {
|
|
9127
9420
|
propertySearchService = inject(PropertySearchService);
|
|
9128
9421
|
loading = false;
|
|
@@ -9597,10 +9890,22 @@ class PropertyMarketViewingIndex extends BaseComponent {
|
|
|
9597
9890
|
stats = computed(() => {
|
|
9598
9891
|
const viewings = this.viewings();
|
|
9599
9892
|
return [
|
|
9600
|
-
{ label: '
|
|
9601
|
-
{
|
|
9602
|
-
|
|
9603
|
-
|
|
9893
|
+
{ label: 'Requests', value: this.length || viewings.length, hint: 'Visits requested while you are acting as a buyer' },
|
|
9894
|
+
{
|
|
9895
|
+
label: 'Pending',
|
|
9896
|
+
value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'PENDING').length,
|
|
9897
|
+
hint: 'Waiting for listing-side review',
|
|
9898
|
+
},
|
|
9899
|
+
{
|
|
9900
|
+
label: 'Approved',
|
|
9901
|
+
value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'APPROVED').length,
|
|
9902
|
+
hint: 'Confirmed appointments in your buyer workspace',
|
|
9903
|
+
},
|
|
9904
|
+
{
|
|
9905
|
+
label: 'Completed',
|
|
9906
|
+
value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'COMPLETED').length,
|
|
9907
|
+
hint: 'Finished visits linked to saved market activity',
|
|
9908
|
+
},
|
|
9604
9909
|
];
|
|
9605
9910
|
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
9606
9911
|
ngOnInit() {
|
|
@@ -9649,7 +9954,7 @@ class PropertyMarketViewingIndex extends BaseComponent {
|
|
|
9649
9954
|
});
|
|
9650
9955
|
}
|
|
9651
9956
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9652
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingIndex, isStandalone: true, selector: "rolatech-property-market-viewing-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"
|
|
9957
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingIndex, isStandalone: true, selector: "rolatech-property-market-viewing-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"workspace\"\n title=\"viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n } } @else if (viewings().length) { @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }] });
|
|
9653
9958
|
}
|
|
9654
9959
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, decorators: [{
|
|
9655
9960
|
type: Component,
|
|
@@ -9660,7 +9965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9660
9965
|
PropertyViewingItemComponent,
|
|
9661
9966
|
MatPaginatorModule,
|
|
9662
9967
|
PropertyWorkspaceShell,
|
|
9663
|
-
], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"
|
|
9968
|
+
], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"workspace\"\n title=\"viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n } } @else if (viewings().length) { @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
|
|
9664
9969
|
}] });
|
|
9665
9970
|
|
|
9666
9971
|
class PropertyMarketViewingDetail {
|
|
@@ -9679,10 +9984,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9679
9984
|
|
|
9680
9985
|
class PropertyMarketOfferIndex extends BaseComponent {
|
|
9681
9986
|
propertyOfferService = inject(PropertyOfferService);
|
|
9682
|
-
breadcrumbs = [
|
|
9683
|
-
{ label: 'Properties', link: '/properties' },
|
|
9684
|
-
{ label: 'Offers' },
|
|
9685
|
-
];
|
|
9987
|
+
breadcrumbs = [{ label: 'Properties', link: '/properties' }, { label: 'Offers' }];
|
|
9686
9988
|
select = 0;
|
|
9687
9989
|
links = [
|
|
9688
9990
|
{ name: 'All', status: '' },
|
|
@@ -9701,10 +10003,22 @@ class PropertyMarketOfferIndex extends BaseComponent {
|
|
|
9701
10003
|
stats = computed(() => {
|
|
9702
10004
|
const offers = this.offers();
|
|
9703
10005
|
return [
|
|
9704
|
-
{ label: '
|
|
9705
|
-
{
|
|
9706
|
-
|
|
9707
|
-
|
|
10006
|
+
{ label: 'Offers', value: this.length || offers.length, hint: 'Offers you submitted while acting as a buyer agent' },
|
|
10007
|
+
{
|
|
10008
|
+
label: 'Submitted',
|
|
10009
|
+
value: offers.filter((item) => item.status === PropertyOfferStatus.SUBMITTED).length,
|
|
10010
|
+
hint: 'Waiting for listing-side review or response',
|
|
10011
|
+
},
|
|
10012
|
+
{
|
|
10013
|
+
label: 'Accepted',
|
|
10014
|
+
value: offers.filter((item) => item.status === PropertyOfferStatus.ACCEPTED).length,
|
|
10015
|
+
hint: 'Offers moved forward in the negotiation flow',
|
|
10016
|
+
},
|
|
10017
|
+
{
|
|
10018
|
+
label: 'Cancelled',
|
|
10019
|
+
value: offers.filter((item) => item.status === PropertyOfferStatus.CANCELLED).length,
|
|
10020
|
+
hint: 'Closed or withdrawn buyer negotiations',
|
|
10021
|
+
},
|
|
9708
10022
|
];
|
|
9709
10023
|
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
9710
10024
|
ngOnInit() {
|
|
@@ -9753,7 +10067,7 @@ class PropertyMarketOfferIndex extends BaseComponent {
|
|
|
9753
10067
|
});
|
|
9754
10068
|
}
|
|
9755
10069
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9756
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferIndex, isStandalone: true, selector: "rolatech-property-market-offer-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"
|
|
10070
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferIndex, isStandalone: true, selector: "rolatech-property-market-offer-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n } } @else if (offers().length > 0) { @for (item of offers(); track item.id) {\n <rolatech-property-offer-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [offer]=\"item\"\n ></rolatech-property-offer-item>\n } } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferItemComponent, selector: "rolatech-property-offer-item", inputs: ["offer"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: OfferItemSkeleton, selector: "rolatech-offer-item-skeleton" }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
9757
10071
|
}
|
|
9758
10072
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, decorators: [{
|
|
9759
10073
|
type: Component,
|
|
@@ -9765,7 +10079,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9765
10079
|
MatPaginatorModule,
|
|
9766
10080
|
OfferItemSkeleton,
|
|
9767
10081
|
PropertyWorkspaceShell,
|
|
9768
|
-
], encapsulation: ViewEncapsulation.None, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"
|
|
10082
|
+
], encapsulation: ViewEncapsulation.None, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n } } @else if (offers().length > 0) { @for (item of offers(); track item.id) {\n <rolatech-property-offer-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [offer]=\"item\"\n ></rolatech-property-offer-item>\n } } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
|
|
9769
10083
|
}] });
|
|
9770
10084
|
|
|
9771
10085
|
class PropertyMarketOfferDetail {
|
|
@@ -9775,11 +10089,11 @@ class PropertyMarketOfferDetail {
|
|
|
9775
10089
|
{ label: 'Offer detail' },
|
|
9776
10090
|
];
|
|
9777
10091
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9778
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyMarketOfferDetail, isStandalone: true, selector: "rolatech-property-market-offer-detail", ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferDetailComponent, selector: "rolatech-property-offer-detail" }] });
|
|
10092
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyMarketOfferDetail, isStandalone: true, selector: "rolatech-property-market-offer-detail", ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail\n [mode]=\"'agent'\"\n eyebrow=\"Market workflow\"\n title=\"Offer detail\"\n description=\"Track buyer-side negotiations, compare counter-offers, and keep every response in the same market workspace.\"\n backLabel=\"Back to buyer offers\"\n ></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferDetailComponent, selector: "rolatech-property-offer-detail", inputs: ["mode", "eyebrow", "title", "description", "backLabel"] }] });
|
|
9779
10093
|
}
|
|
9780
10094
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, decorators: [{
|
|
9781
10095
|
type: Component,
|
|
9782
|
-
args: [{ selector: 'rolatech-property-market-offer-detail', imports: [CommonModule, PropertyMarketBreadcrumbComponent, PropertyOfferDetailComponent], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"] }]
|
|
10096
|
+
args: [{ selector: 'rolatech-property-market-offer-detail', imports: [CommonModule, PropertyMarketBreadcrumbComponent, PropertyOfferDetailComponent], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail\n [mode]=\"'agent'\"\n eyebrow=\"Market workflow\"\n title=\"Offer detail\"\n description=\"Track buyer-side negotiations, compare counter-offers, and keep every response in the same market workspace.\"\n backLabel=\"Back to buyer offers\"\n ></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"] }]
|
|
9783
10097
|
}] });
|
|
9784
10098
|
|
|
9785
10099
|
class PropertyMarketViewingRequest extends BaseComponent {
|
|
@@ -9872,7 +10186,7 @@ class PropertyMarketViewingRequest extends BaseComponent {
|
|
|
9872
10186
|
this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
|
|
9873
10187
|
}
|
|
9874
10188
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9875
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingRequest, isStandalone: true, selector: "rolatech-letting-viewing-request", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three\n viewing options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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" }] });
|
|
10189
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingRequest, isStandalone: true, selector: "rolatech-letting-viewing-request", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three viewing\n options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\"> {{ applicantType.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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
10190
|
}
|
|
9877
10191
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, decorators: [{
|
|
9878
10192
|
type: Component,
|
|
@@ -9891,7 +10205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9891
10205
|
PropertyPricingComponent,
|
|
9892
10206
|
PropertyMarketBreadcrumbComponent,
|
|
9893
10207
|
MatSelectModule,
|
|
9894
|
-
], template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three\n viewing options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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"] }]
|
|
10208
|
+
], template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three viewing\n options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\"> {{ applicantType.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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"] }]
|
|
9895
10209
|
}], ctorParameters: () => [] });
|
|
9896
10210
|
|
|
9897
10211
|
class PropertyMarketOfferRequest extends BaseComponent {
|
|
@@ -9989,7 +10303,7 @@ class PropertyMarketOfferRequest extends BaseComponent {
|
|
|
9989
10303
|
}
|
|
9990
10304
|
}
|
|
9991
10305
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9992
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferRequest, isStandalone: true, selector: "rolatech-property-market-offer-request", providers: [OfferFormFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n
|
|
10306
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferRequest, isStandalone: true, selector: "rolatech-property-market-offer-request", providers: [OfferFormFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review, approval, or\n counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs that\n take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
|
|
9993
10307
|
}
|
|
9994
10308
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, decorators: [{
|
|
9995
10309
|
type: Component,
|
|
@@ -10002,7 +10316,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
10002
10316
|
PropertyMarketBreadcrumbComponent,
|
|
10003
10317
|
PropertyOfferRentalForm,
|
|
10004
10318
|
PropertyOfferSaleForm,
|
|
10005
|
-
], providers: [OfferFormFacade], template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n
|
|
10319
|
+
], providers: [OfferFormFacade], template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review, approval, or\n counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs that\n take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"] }]
|
|
10006
10320
|
}], ctorParameters: () => [] });
|
|
10007
10321
|
|
|
10008
10322
|
class PropertyMarketSavedIndex {
|
|
@@ -10360,72 +10674,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
10360
10674
|
], template: "<rolatech-property-workspace-shell\n eyebrow=\"Agent workspace\"\n title=\"Listing viewings\"\n description=\"Keep buyer appointments organized across your managed listings, review the latest requests, and move straight into viewing detail when something changes.\"\n [stats]=\"stats()\"\n>\n <nav workspace-filters class=\"property-listing-viewing-index__status-nav\" aria-label=\"Listing viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-listing-viewing-index__status-link\"\n [class.property-listing-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-listing-viewing-index__list\">\n @if (loading) {\n @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-listing-viewing-index__placeholder\"></div>\n }\n } @else if (viewings().length) {\n @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [viewing]=\"item\"></rolatech-property-viewing-item>\n }\n } @else {\n <div class=\"property-listing-viewing-index__empty\">\n <h3 i18n>No viewing activity yet</h3>\n <p i18n>Incoming listing appointments will appear here once buyers or partner agents send requests.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</rolatech-property-workspace-shell>\n", styles: [":host{display:block;padding:1rem}.property-listing-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-listing-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.property-listing-viewing-index__status-link:hover,.property-listing-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-listing-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-listing-viewing-index__placeholder,.property-listing-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-listing-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-listing-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-listing-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){:host{padding:1.25rem}}\n"] }]
|
|
10361
10675
|
}] });
|
|
10362
10676
|
|
|
10363
|
-
class PropertyListingViewingDetail
|
|
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]] });
|
|
10677
|
+
class PropertyListingViewingDetail {
|
|
10678
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10679
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyListingViewingDetail, isStandalone: true, selector: "rolatech-property-listing-viewing-detail", ngImport: i0, template: "<rolatech-property-manage-viewings-detail></rolatech-property-manage-viewings-detail>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyManageViewingsDetailComponent, selector: "rolatech-property-manage-viewings-detail" }] });
|
|
10425
10680
|
}
|
|
10426
10681
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingViewingDetail, decorators: [{
|
|
10427
10682
|
type: Component,
|
|
10428
|
-
args: [{ selector: 'rolatech-property-listing-viewing-detail', imports: [CommonModule,
|
|
10683
|
+
args: [{ selector: 'rolatech-property-listing-viewing-detail', imports: [CommonModule, PropertyManageViewingsDetailComponent], template: "<rolatech-property-manage-viewings-detail></rolatech-property-manage-viewings-detail>\n" }]
|
|
10429
10684
|
}] });
|
|
10430
10685
|
|
|
10431
10686
|
const propertyListingViewingRoutes = [
|
|
@@ -10475,7 +10730,7 @@ class PropertyListingOfferDetail extends BaseComponent {
|
|
|
10475
10730
|
this.propertyOfferService.getOffer(this.id).subscribe({
|
|
10476
10731
|
next: (res) => {
|
|
10477
10732
|
this.offer = res.data;
|
|
10478
|
-
|
|
10733
|
+
this.name = this.applicantName();
|
|
10479
10734
|
this.getProperty(this.offer.propertyId);
|
|
10480
10735
|
},
|
|
10481
10736
|
});
|
|
@@ -10560,7 +10815,9 @@ class PropertyListingOfferDetail extends BaseComponent {
|
|
|
10560
10815
|
}
|
|
10561
10816
|
});
|
|
10562
10817
|
}
|
|
10563
|
-
counter() {
|
|
10818
|
+
counter() {
|
|
10819
|
+
this.f.enterEdit();
|
|
10820
|
+
}
|
|
10564
10821
|
rfPassed() {
|
|
10565
10822
|
const dialogRef = this.dialog.open(AcceptDialogComponent, {
|
|
10566
10823
|
width: '400px',
|
|
@@ -10671,19 +10928,58 @@ class PropertyListingOfferDetail extends BaseComponent {
|
|
|
10671
10928
|
},
|
|
10672
10929
|
});
|
|
10673
10930
|
}
|
|
10931
|
+
applicantName() {
|
|
10932
|
+
const primaryTenant = this.offer?.tenants?.[0];
|
|
10933
|
+
return primaryTenant?.fullName || this.offer?.sale?.buyerName || this.name || 'Applicant';
|
|
10934
|
+
}
|
|
10935
|
+
applicantContactSummary() {
|
|
10936
|
+
if (!this.offer) {
|
|
10937
|
+
return 'Contact details appear here once the offer loads.';
|
|
10938
|
+
}
|
|
10939
|
+
const email = this.applicantEmail();
|
|
10940
|
+
const phone = this.applicantPhone();
|
|
10941
|
+
if (email && phone) {
|
|
10942
|
+
return `${email} · ${phone}`;
|
|
10943
|
+
}
|
|
10944
|
+
return email || phone || 'Contact details not shared';
|
|
10945
|
+
}
|
|
10946
|
+
agentName() {
|
|
10947
|
+
return this.agent?.name || 'Assigned agent';
|
|
10948
|
+
}
|
|
10949
|
+
agentEmail() {
|
|
10950
|
+
return this.agent?.email || 'Not shared';
|
|
10951
|
+
}
|
|
10952
|
+
agentPhone() {
|
|
10953
|
+
return this.agent?.phone || 'Not shared';
|
|
10954
|
+
}
|
|
10955
|
+
propertyMediaUrl() {
|
|
10956
|
+
return this.property?.media?.[0]?.url || '';
|
|
10957
|
+
}
|
|
10958
|
+
applicantEmail() {
|
|
10959
|
+
return this.offer?.tenants?.[0]?.email || this.offer?.sale?.email || '';
|
|
10960
|
+
}
|
|
10961
|
+
applicantPhone() {
|
|
10962
|
+
return this.offer?.tenants?.[0]?.phone || this.offer?.sale?.phone || '';
|
|
10963
|
+
}
|
|
10964
|
+
targetDate() {
|
|
10965
|
+
return this.offer?.rentalTerms?.moveInDate || this.offer?.sale?.proposedExchangeDate || null;
|
|
10966
|
+
}
|
|
10674
10967
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingOfferDetail, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
10675
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingOfferDetail, isStandalone: true, selector: "rolatech-property-listing-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], usesInheritance: true, ngImport: i0, template: "@if (offer) {\n<rolatech-toolbar [title]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4 h-fit\">\n <!-- Primary negotiation actions -->\n <rolatech-offer-negotiation-section />\n\n <rolatech-offer-item-card />\n\n <!-- Tenants card -->\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-(--rt-border-color) bg-(--rt-surface-2) p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <!-- Offer references -->\n <rolatech-offer-reference-provider />\n\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">Counter Offer</button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n</mat-menu>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferReferenceProvider, selector: "rolatech-offer-reference-provider", inputs: ["provider", "providerOther"], outputs: ["providerChange", "providerOtherChange"] }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferInternalNoteCard, selector: "rolatech-offer-internal-note-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }] });
|
|
10968
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyListingOfferDetail, isStandalone: true, selector: "rolatech-property-listing-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], usesInheritance: true, ngImport: i0, template: "@if (offer) {\n<div class=\"property-listing-offer-detail\">\n <rolatech-toolbar [title]=\"'Offer detail'\" [subtitle]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button type=\"button\" (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <section class=\"property-listing-offer-detail__hero\">\n <div class=\"property-listing-offer-detail__hero-copy\">\n <span class=\"property-listing-offer-detail__eyebrow\" i18n>Agent workspace</span>\n\n <div class=\"property-listing-offer-detail__hero-heading\">\n <h1 class=\"property-listing-offer-detail__title\">{{ property.title || 'Offer detail' }}</h1>\n <span class=\"property-listing-offer-detail__status\">{{ statusLabel() }}</span>\n </div>\n\n <p class=\"property-listing-offer-detail__intro\" i18n>\n Keep negotiation, billing, referencing, and applicant coordination aligned from one offer workspace.\n </p>\n\n <div class=\"property-listing-offer-detail__fact-grid\">\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Offer amount</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.item.amount | price }}</strong>\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ targetDate() ? (targetDate() | date: 'mediumDate') : 'Not shared' }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Invoice option</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ invoiceOptionLabel[invoiceOptionDraft() || offer.invoiceOption] }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Applicants</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.tenants?.length ?? 1 }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__hero-side\">\n <article class=\"property-listing-offer-detail__property-card\">\n <div class=\"property-listing-offer-detail__media\">\n @if (propertyMediaUrl()) { @defer {\n <rolatech-thumbnail [src]=\"propertyMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-listing-offer-detail__media-fallback\"></div>\n } } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-listing-offer-detail__property-copy\">\n <div class=\"property-listing-offer-detail__property-price\">{{ (property.price || offer.item.amount) | price }}</div>\n @if (property) {\n <div class=\"property-listing-offer-detail__property-facts\">\n <span>{{ property.bedrooms }} bed</span>\n <span>{{ property.bathrooms }} bath</span>\n <span>{{ property.receptions }} reception</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"property-listing-offer-detail__summary-card\">\n <span class=\"property-listing-offer-detail__summary-label\" i18n>Applicant</span>\n <strong class=\"property-listing-offer-detail__summary-value\">{{ applicantName() }}</strong>\n <span class=\"property-listing-offer-detail__summary-contact\">{{ applicantContactSummary() }}</span>\n </article>\n </div>\n </section>\n\n <div class=\"property-listing-offer-detail__content\">\n <main class=\"property-listing-offer-detail__main\">\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </main>\n\n <aside class=\"property-listing-offer-detail__side\">\n <div class=\"property-listing-offer-detail__sticky\">\n <section class=\"property-listing-offer-detail__panel\">\n <div class=\"property-listing-offer-detail__panel-head\">\n <div>\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Billing</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move-in invoice handling</h2>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <p class=\"property-listing-offer-detail__summary-note\" i18n>\n Choose whether security deposit and first rent land on a combined invoice or separate invoices.\n </p>\n\n <div class=\"property-listing-offer-detail__radio-grid\">\n <mat-radio-group\n class=\"block\"\n [value]=\"invoiceOptionDraft() || offer.invoiceOption\"\n (change)=\"setInvoiceOption($event.value)\"\n >\n <div class=\"grid gap-3\">\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with security deposit and first rent on separate lines.\n </div>\n </div>\n </div>\n </label>\n\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Issue separate invoices so move-in payments can be tracked independently.\n </div>\n </div>\n </div>\n </label>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <rolatech-offer-reference-provider />\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Applicant contact</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ applicantName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ applicantEmail() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ applicantPhone() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>User id</span>\n <strong>{{ offer.userId || 'Not attached' }}</strong>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__action-row\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantEmail()\" (click)=\"emailApplicant(applicantEmail())\">\n Email\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantPhone()\" (click)=\"callApplicant(applicantPhone())\">\n Call\n </button>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Listing agent</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ agentName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Agent id</span>\n <strong>{{ offer.agentId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Quick actions</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move the negotiation forward</h2>\n\n <div class=\"property-listing-offer-detail__stack-actions\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button type=\"button\" (click)=\"rfPassed()\">References passed</button>\n <button mat-stroked-button type=\"button\" (click)=\"rfFailed()\">References failed</button>\n }\n </div>\n </section>\n </div>\n </aside>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">\n Counter Offer\n </button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n </mat-menu>\n</div>\n}\n", styles: [":host{display:block}.property-listing-offer-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%),var(--rt-base-background, #ffffff))}.property-listing-offer-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-listing-offer-detail__hero-copy,.property-listing-offer-detail__hero-side,.property-listing-offer-detail__main,.property-listing-offer-detail__side,.property-listing-offer-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-listing-offer-detail__eyebrow,.property-listing-offer-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-listing-offer-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-listing-offer-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-listing-offer-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.82rem;font-weight:700}.property-listing-offer-detail__intro,.property-listing-offer-detail__summary-note,.property-listing-offer-detail__summary-contact{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-listing-offer-detail__intro{color:var(--rt-text-primary);font-size:1.02rem;font-weight:700}.property-listing-offer-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-listing-offer-detail__fact,.property-listing-offer-detail__summary-card,.property-listing-offer-detail__property-card,.property-listing-offer-detail__panel,.property-listing-offer-detail__radio-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-offer-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-listing-offer-detail__fact-label,.property-listing-offer-detail__summary-label,.property-listing-offer-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-listing-offer-detail__fact-value,.property-listing-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-listing-offer-detail__property-card{display:flex;flex-direction:column;gap:.95rem;padding:1rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-listing-offer-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-listing-offer-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-listing-offer-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-listing-offer-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-listing-offer-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-listing-offer-detail__summary-card,.property-listing-offer-detail__panel{display:flex;flex-direction:column;gap:.4rem;padding:1rem 1.05rem}.property-listing-offer-detail__summary-value,.property-listing-offer-detail__summary-contact,.property-listing-offer-detail__summary-list strong{overflow-wrap:anywhere;word-break:break-word}.property-listing-offer-detail__content{display:grid;gap:1rem}.property-listing-offer-detail__panel{border-radius:1.5rem;padding:1.25rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-listing-offer-detail__panel-head{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-listing-offer-detail__radio-grid{margin-top:.5rem}.property-listing-offer-detail__radio-card{display:block;padding:.95rem 1rem}.property-listing-offer-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-listing-offer-detail__action-row,.property-listing-offer-detail__stack-actions{display:grid;gap:.75rem}@media(min-width:640px){.property-listing-offer-detail__action-row{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:768px){.property-listing-offer-detail{padding:1.25rem}.property-listing-offer-detail__hero{grid-template-columns:minmax(0,1.5fr) minmax(18rem,.9fr);align-items:stretch;padding:1.5rem}.property-listing-offer-detail__hero-heading,.property-listing-offer-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-listing-offer-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-listing-offer-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-listing-offer-detail__fact-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i2$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i2$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i2$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$2.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$2.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferReferenceProvider, selector: "rolatech-offer-reference-provider", inputs: ["provider", "providerOther"], outputs: ["providerChange", "providerOtherChange"] }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferInternalNoteCard, selector: "rolatech-offer-internal-note-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
10676
10969
|
}
|
|
10677
10970
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyListingOfferDetail, decorators: [{
|
|
10678
10971
|
type: Component,
|
|
10679
10972
|
args: [{ selector: 'rolatech-property-listing-offer-detail', imports: [
|
|
10680
10973
|
CommonModule,
|
|
10681
10974
|
ToolbarComponent,
|
|
10975
|
+
ThumbnailComponent,
|
|
10976
|
+
ImagePlaceholderComponent,
|
|
10682
10977
|
MatButtonModule,
|
|
10683
10978
|
MatIcon,
|
|
10684
10979
|
MatMenuModule,
|
|
10685
10980
|
MatRadioModule,
|
|
10686
10981
|
FormsModule,
|
|
10982
|
+
PricePipe,
|
|
10687
10983
|
OfferRentalTermsCard,
|
|
10688
10984
|
OfferTenantsAccordion,
|
|
10689
10985
|
OfferItemCard,
|
|
@@ -10694,7 +10990,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
10694
10990
|
OfferCounterLatestCard,
|
|
10695
10991
|
OfferCounterPreviousCard,
|
|
10696
10992
|
OfferNegotiationSection,
|
|
10697
|
-
], providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], template: "@if (offer) {\n<rolatech-toolbar [title]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4 h-fit\">\n <!-- Primary negotiation actions -->\n <rolatech-offer-negotiation-section />\n\n <rolatech-offer-item-card />\n\n <!-- Tenants card -->\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <!-- Updated offer -->\n <rolatech-offer-counter-latest-card />\n <!-- Previous Offer (Grey) -->\n <rolatech-offer-counter-previous-card />\n }\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-(--rt-border-color) bg-(--rt-surface-2) p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-base-background) p-3 hover:border-(--rt-border-color) transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <!-- Offer references -->\n <rolatech-offer-reference-provider />\n\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border border-(--rt-border-color) bg-(--rt-surface-2)\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-(--rt-text-secondary)\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n } @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-(--rt-text-secondary)\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-(--rt-border-color) bg-(--rt-base-background) p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">Counter Offer</button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n</mat-menu>\n}\n" }]
|
|
10993
|
+
], providers: [OfferDetailContext, PropertyOfferNegotiationFacade, OfferInternalNoteFacade], template: "@if (offer) {\n<div class=\"property-listing-offer-detail\">\n <rolatech-toolbar [title]=\"'Offer detail'\" [subtitle]=\"statusLabel()\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button type=\"button\" (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <section class=\"property-listing-offer-detail__hero\">\n <div class=\"property-listing-offer-detail__hero-copy\">\n <span class=\"property-listing-offer-detail__eyebrow\" i18n>Agent workspace</span>\n\n <div class=\"property-listing-offer-detail__hero-heading\">\n <h1 class=\"property-listing-offer-detail__title\">{{ property.title || 'Offer detail' }}</h1>\n <span class=\"property-listing-offer-detail__status\">{{ statusLabel() }}</span>\n </div>\n\n <p class=\"property-listing-offer-detail__intro\" i18n>\n Keep negotiation, billing, referencing, and applicant coordination aligned from one offer workspace.\n </p>\n\n <div class=\"property-listing-offer-detail__fact-grid\">\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Offer amount</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.item.amount | price }}</strong>\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ targetDate() ? (targetDate() | date: 'mediumDate') : 'Not shared' }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Invoice option</span>\n <strong class=\"property-listing-offer-detail__fact-value\"\n >{{ invoiceOptionLabel[invoiceOptionDraft() || offer.invoiceOption] }}</strong\n >\n </article>\n\n <article class=\"property-listing-offer-detail__fact\">\n <span class=\"property-listing-offer-detail__fact-label\" i18n>Applicants</span>\n <strong class=\"property-listing-offer-detail__fact-value\">{{ offer.tenants?.length ?? 1 }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__hero-side\">\n <article class=\"property-listing-offer-detail__property-card\">\n <div class=\"property-listing-offer-detail__media\">\n @if (propertyMediaUrl()) { @defer {\n <rolatech-thumbnail [src]=\"propertyMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-listing-offer-detail__media-fallback\"></div>\n } } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-listing-offer-detail__property-copy\">\n <div class=\"property-listing-offer-detail__property-price\">{{ (property.price || offer.item.amount) | price }}</div>\n @if (property) {\n <div class=\"property-listing-offer-detail__property-facts\">\n <span>{{ property.bedrooms }} bed</span>\n <span>{{ property.bathrooms }} bath</span>\n <span>{{ property.receptions }} reception</span>\n </div>\n }\n </div>\n </article>\n\n <article class=\"property-listing-offer-detail__summary-card\">\n <span class=\"property-listing-offer-detail__summary-label\" i18n>Applicant</span>\n <strong class=\"property-listing-offer-detail__summary-value\">{{ applicantName() }}</strong>\n <span class=\"property-listing-offer-detail__summary-contact\">{{ applicantContactSummary() }}</span>\n </article>\n </div>\n </section>\n\n <div class=\"property-listing-offer-detail__content\">\n <main class=\"property-listing-offer-detail__main\">\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) { @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n } @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n <rolatech-offer-internal-note-card />\n </main>\n\n <aside class=\"property-listing-offer-detail__side\">\n <div class=\"property-listing-offer-detail__sticky\">\n <section class=\"property-listing-offer-detail__panel\">\n <div class=\"property-listing-offer-detail__panel-head\">\n <div>\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Billing</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move-in invoice handling</h2>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <p class=\"property-listing-offer-detail__summary-note\" i18n>\n Choose whether security deposit and first rent land on a combined invoice or separate invoices.\n </p>\n\n <div class=\"property-listing-offer-detail__radio-grid\">\n <mat-radio-group\n class=\"block\"\n [value]=\"invoiceOptionDraft() || offer.invoiceOption\"\n (change)=\"setInvoiceOption($event.value)\"\n >\n <div class=\"grid gap-3\">\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n One invoice with security deposit and first rent on separate lines.\n </div>\n </div>\n </div>\n </label>\n\n <label class=\"property-listing-offer-detail__radio-card\">\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-(--rt-text-secondary)\">\n Issue separate invoices so move-in payments can be tracked independently.\n </div>\n </div>\n </div>\n </label>\n </div>\n </mat-radio-group>\n </div>\n </section>\n\n <rolatech-offer-reference-provider />\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Applicant contact</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ applicantName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ applicantEmail() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ applicantPhone() || 'Not shared' }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>User id</span>\n <strong>{{ offer.userId || 'Not attached' }}</strong>\n </div>\n </div>\n\n <div class=\"property-listing-offer-detail__action-row\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantEmail()\" (click)=\"emailApplicant(applicantEmail())\">\n Email\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!applicantPhone()\" (click)=\"callApplicant(applicantPhone())\">\n Call\n </button>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Listing agent</span>\n <h2 class=\"property-listing-offer-detail__panel-title\">{{ agentName() }}</h2>\n\n <div class=\"property-listing-offer-detail__summary-list\">\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Email</span>\n <strong>{{ agentEmail() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Phone</span>\n <strong>{{ agentPhone() }}</strong>\n </div>\n <div>\n <span class=\"property-listing-offer-detail__summary-key\" i18n>Agent id</span>\n <strong>{{ offer.agentId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-listing-offer-detail__panel property-listing-offer-detail__panel--subtle\">\n <span class=\"property-listing-offer-detail__panel-eyebrow\" i18n>Quick actions</span>\n <h2 class=\"property-listing-offer-detail__panel-title\" i18n>Move the negotiation forward</h2>\n\n <div class=\"property-listing-offer-detail__stack-actions\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" (click)=\"reject()\">Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button type=\"button\" (click)=\"rfPassed()\">References passed</button>\n <button mat-stroked-button type=\"button\" (click)=\"rfFailed()\">References failed</button>\n }\n </div>\n </section>\n </div>\n </aside>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (f.permissions()?.canCounter) {\n <button mat-menu-item class=\"rounded-full border border-(--rt-border-color) px-4 py-2\" (click)=\"f.enterEdit()\">\n Counter Offer\n </button>\n } @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n } @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n </mat-menu>\n</div>\n}\n", styles: [":host{display:block}.property-listing-offer-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%),var(--rt-base-background, #ffffff))}.property-listing-offer-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-listing-offer-detail__hero-copy,.property-listing-offer-detail__hero-side,.property-listing-offer-detail__main,.property-listing-offer-detail__side,.property-listing-offer-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-listing-offer-detail__eyebrow,.property-listing-offer-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-listing-offer-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-listing-offer-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-listing-offer-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.82rem;font-weight:700}.property-listing-offer-detail__intro,.property-listing-offer-detail__summary-note,.property-listing-offer-detail__summary-contact{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-listing-offer-detail__intro{color:var(--rt-text-primary);font-size:1.02rem;font-weight:700}.property-listing-offer-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-listing-offer-detail__fact,.property-listing-offer-detail__summary-card,.property-listing-offer-detail__property-card,.property-listing-offer-detail__panel,.property-listing-offer-detail__radio-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-listing-offer-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-listing-offer-detail__fact-label,.property-listing-offer-detail__summary-label,.property-listing-offer-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-listing-offer-detail__fact-value,.property-listing-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-listing-offer-detail__property-card{display:flex;flex-direction:column;gap:.95rem;padding:1rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-listing-offer-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-listing-offer-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-listing-offer-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-listing-offer-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-listing-offer-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-listing-offer-detail__summary-card,.property-listing-offer-detail__panel{display:flex;flex-direction:column;gap:.4rem;padding:1rem 1.05rem}.property-listing-offer-detail__summary-value,.property-listing-offer-detail__summary-contact,.property-listing-offer-detail__summary-list strong{overflow-wrap:anywhere;word-break:break-word}.property-listing-offer-detail__content{display:grid;gap:1rem}.property-listing-offer-detail__panel{border-radius:1.5rem;padding:1.25rem;box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-listing-offer-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-listing-offer-detail__panel-head{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-listing-offer-detail__radio-grid{margin-top:.5rem}.property-listing-offer-detail__radio-card{display:block;padding:.95rem 1rem}.property-listing-offer-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-listing-offer-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-listing-offer-detail__action-row,.property-listing-offer-detail__stack-actions{display:grid;gap:.75rem}@media(min-width:640px){.property-listing-offer-detail__action-row{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:768px){.property-listing-offer-detail{padding:1.25rem}.property-listing-offer-detail__hero{grid-template-columns:minmax(0,1.5fr) minmax(18rem,.9fr);align-items:stretch;padding:1.5rem}.property-listing-offer-detail__hero-heading,.property-listing-offer-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-listing-offer-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-listing-offer-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-listing-offer-detail__fact-grid{grid-template-columns:1fr}}\n"] }]
|
|
10698
10994
|
}] });
|
|
10699
10995
|
|
|
10700
10996
|
class PropertyListingOfferIndex extends BaseComponent {
|
|
@@ -11359,4 +11655,4 @@ function provideAngularPropertyFeature() {
|
|
|
11359
11655
|
*/
|
|
11360
11656
|
|
|
11361
11657
|
export { PropertyScope as A, PropertyViewingStatus as B, PropertyViewerCategory as C, propertyViewingStatusCode as D, propertyViewingStatusLabel as E, PropertyOfferTimelineStatus as F, PropertyOfferType as G, PropertyOfferStatus as H, EmploymentStatus as I, ResidencyStatus as J, PropertyApplicantType as K, AdverseCreditStatus as L, Market as M, BedroomRange as N, PriceRange as O, PropertyUtil as P, RentFrequency as R, ViewingTime as V, PropertyViewType as a, PropertyManageItemSkeleton as b, PropertyViewingItemComponent as c, propertyManageOffersRoutes as d, propertyRoutes as e, featureManageRoutes as f, propertyManageRoutes as g, propertyManageViewingsRoutes as h, propertyMarketRoutes as i, propertyListingViewingRoutes as j, propertyListingOfferRoutes as k, propertyAgentOverviewRoutes as l, propertyManageOverviewRoutes as m, propertyListingRoutes as n, provideAngularProperty as o, propertyReviewRoutes as p, provideAngularPropertyFeature as q, PropertyActionsComponent as r, PropertyItemComponent as s, tenantManageRoutes as t, PropertyPricingComponent as u, PropertyPriceType as v, PropertyVideoProvider as w, PropertyStatus as x, PropertyType as y, PropertyInventoryStatus as z };
|
|
11362
|
-
//# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-
|
|
11658
|
+
//# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-zL_Uw36W.mjs.map
|