@rolatech/angular-property 20.3.2-beta.0 → 20.3.2-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{rolatech-angular-property-property-index.component-CbvPpms7.mjs → rolatech-angular-property-property-index.component-2BXmI0uJ.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-index.component-CbvPpms7.mjs.map → rolatech-angular-property-property-index.component-2BXmI0uJ.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-Bvxmxhbu.mjs → rolatech-angular-property-property-manage-viewings-index.component-Cws6ElRk.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-Bvxmxhbu.mjs.map → rolatech-angular-property-property-manage-viewings-index.component-Cws6ElRk.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-rolatech-angular-property-BipC43h_.mjs → rolatech-angular-property-rolatech-angular-property-CKEFhpfX.mjs} +526 -413
- package/fesm2022/rolatech-angular-property-rolatech-angular-property-CKEFhpfX.mjs.map +1 -0
- package/fesm2022/rolatech-angular-property.mjs +1 -1
- package/package.json +6 -6
- package/themes/_default.scss +1 -1
- package/fesm2022/rolatech-angular-property-rolatech-angular-property-BipC43h_.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';
|
|
@@ -1138,7 +1139,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1138
1139
|
], template: "<rolatech-toolbar title=\"Offers\">\n <div class=\"flex items-center gap-2\">\n <button mat-icon-button (click)=\"toggleSearch()\">\n <mat-icon>search</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"p-2\">\n <rolatech-search-bar\n [(show)]=\"open\"\n placeholder=\"Search offer by offer id or address\"\n #searchBar\n (search)=\"searchByText($event)\"\n (close)=\"onCloseSearch()\"\n ></rolatech-search-bar>\n</div>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n</rolatech-tabs>\n@if (loading) {\n <div class=\"divide-y divide-(--rt-10-percent-layer) flex flex-col px-3\">\n @for (row of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n }\n </div>\n} @else {\n <div class=\"space-y-2 p-2\">\n @if (offers() && offers().length > 0) {\n @for (item of offers(); track item) {\n <rolatech-property-offer-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [offer]=\"item\"></rolatech-property-offer-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </div>\n}\n<mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n>\n</mat-paginator>\n" }]
|
|
1139
1140
|
}] });
|
|
1140
1141
|
|
|
1141
|
-
const MY_FORMATS$
|
|
1142
|
+
const MY_FORMATS$7 = {
|
|
1142
1143
|
parse: {
|
|
1143
1144
|
dateInput: 'YYYY-MM-DD',
|
|
1144
1145
|
},
|
|
@@ -1163,7 +1164,7 @@ class OfferEdit extends BaseComponent {
|
|
|
1163
1164
|
useClass: MomentDateAdapter,
|
|
1164
1165
|
deps: [MAT_DATE_LOCALE],
|
|
1165
1166
|
},
|
|
1166
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1167
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
1167
1168
|
], usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"offer().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"offer().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"offer().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"offer().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Offer amount</mat-label>\n <input matInput [(ngModel)]=\"offer().amount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"offer().startDate\"\n (dateInput)=\"offer().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatOptionModule }] });
|
|
1168
1169
|
}
|
|
1169
1170
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferEdit, decorators: [{
|
|
@@ -1174,7 +1175,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1174
1175
|
useClass: MomentDateAdapter,
|
|
1175
1176
|
deps: [MAT_DATE_LOCALE],
|
|
1176
1177
|
},
|
|
1177
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1178
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
1178
1179
|
], template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"offer().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"offer().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"offer().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"offer().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Offer amount</mat-label>\n <input matInput [(ngModel)]=\"offer().amount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"offer().startDate\"\n (dateInput)=\"offer().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
|
|
1179
1180
|
}], propDecorators: { offer: [{ type: i0.Input, args: [{ isSignal: true, alias: "offer", required: true }] }, { type: i0.Output, args: ["offerChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
|
|
1180
1181
|
|
|
@@ -1336,12 +1337,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1336
1337
|
|
|
1337
1338
|
class OfferRentalTermsCard {
|
|
1338
1339
|
ctx = inject(OfferDetailContext);
|
|
1340
|
+
humanize(value) {
|
|
1341
|
+
if (!value) {
|
|
1342
|
+
return '—';
|
|
1343
|
+
}
|
|
1344
|
+
return value
|
|
1345
|
+
.trim()
|
|
1346
|
+
.replace(/[_-]+/g, ' ')
|
|
1347
|
+
.toLowerCase()
|
|
1348
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1349
|
+
}
|
|
1350
|
+
boolLabel(value) {
|
|
1351
|
+
if (value === null || value === undefined) {
|
|
1352
|
+
return '—';
|
|
1353
|
+
}
|
|
1354
|
+
return value ? 'Yes' : 'No';
|
|
1355
|
+
}
|
|
1339
1356
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1340
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1357
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-rental-terms-card\">\n <div class=\"offer-rental-terms-card__head\">\n <div>\n <span class=\"offer-rental-terms-card__eyebrow\">Submitted terms</span>\n <h3 class=\"offer-rental-terms-card__title\">Original rental offer</h3>\n <p class=\"offer-rental-terms-card__description\">These were the first rental terms attached to the submission.</p>\n </div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"offer-rental-terms-card__grid\">\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Offer amount</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Move-in date</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Payment frequency</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Tenancy length</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.tenancyLengthMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Break clause</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.breakClauseMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Pets</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Smoker</span>\n <strong class=\"offer-rental-terms-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Furniture</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.furnitureRequirement) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field offer-rental-terms-card__field--wide\">\n <span class=\"offer-rental-terms-card__label\">Additional requests</span>\n <strong class=\"offer-rental-terms-card__value offer-rental-terms-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-rental-terms-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-rental-terms-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-rental-terms-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-rental-terms-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-rental-terms-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-rental-terms-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-rental-terms-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-rental-terms-card__field--wide{grid-column:1/-1}.offer-rental-terms-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-rental-terms-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-rental-terms-card__value--multiline{white-space:pre-wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
1341
1358
|
}
|
|
1342
1359
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferRentalTermsCard, decorators: [{
|
|
1343
1360
|
type: Component,
|
|
1344
|
-
args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1361
|
+
args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-rental-terms-card\">\n <div class=\"offer-rental-terms-card__head\">\n <div>\n <span class=\"offer-rental-terms-card__eyebrow\">Submitted terms</span>\n <h3 class=\"offer-rental-terms-card__title\">Original rental offer</h3>\n <p class=\"offer-rental-terms-card__description\">These were the first rental terms attached to the submission.</p>\n </div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"offer-rental-terms-card__grid\">\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Offer amount</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Move-in date</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Payment frequency</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Tenancy length</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.tenancyLengthMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Break clause</span>\n <strong class=\"offer-rental-terms-card__value\">{{ ctx.asText(rt.breakClauseMonths) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Pets</span>\n <strong class=\"offer-rental-terms-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Smoker</span>\n <strong class=\"offer-rental-terms-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field\">\n <span class=\"offer-rental-terms-card__label\">Furniture</span>\n <strong class=\"offer-rental-terms-card__value\">{{ humanize(rt.furnitureRequirement) }}</strong>\n </article>\n <article class=\"offer-rental-terms-card__field offer-rental-terms-card__field--wide\">\n <span class=\"offer-rental-terms-card__label\">Additional requests</span>\n <strong class=\"offer-rental-terms-card__value offer-rental-terms-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-rental-terms-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-rental-terms-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-rental-terms-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-rental-terms-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-rental-terms-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-rental-terms-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-rental-terms-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-rental-terms-card__field--wide{grid-column:1/-1}.offer-rental-terms-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-rental-terms-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-rental-terms-card__value--multiline{white-space:pre-wrap}\n"] }]
|
|
1345
1362
|
}] });
|
|
1346
1363
|
|
|
1347
1364
|
class OfferTenantExpanded {
|
|
@@ -1413,11 +1430,11 @@ class OfferTenantCard {
|
|
|
1413
1430
|
this.onToggleInput()(this.index());
|
|
1414
1431
|
}
|
|
1415
1432
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1416
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantCard, isStandalone: true, selector: "rolatech-offer-tenant-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"
|
|
1433
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantCard, isStandalone: true, selector: "rolatech-offer-tenant-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"offer-tenant-card\">\n <button\n type=\"button\"\n class=\"offer-tenant-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-tenant-card__copy\">\n <div class=\"offer-tenant-card__topline\">Applicant {{ index() + 1 }}</div>\n <div class=\"offer-tenant-card__name\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"offer-tenant-card__contact\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"offer-tenant-card__chips\">\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"offer-tenant-card__arrow\" [class.offer-tenant-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-tenant-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-tenant-card__body\">\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-tenant-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-tenant-card__toggle{display:flex;width:100%;padding:1rem;gap:.9rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-tenant-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-tenant-card__copy{display:flex;flex:1;min-width:0;flex-direction:column;gap:.35rem}.offer-tenant-card__topline{color:var(--rt-brand-color);font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.offer-tenant-card__name{color:var(--rt-text-primary);font-size:1rem;font-weight:700;line-height:1.3}.offer-tenant-card__contact{color:var(--rt-text-secondary);font-size:.82rem;line-height:1.5}.offer-tenant-card__chips{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.2rem}.offer-tenant-card__chip{display:inline-flex;align-items:center;min-height:1.85rem;padding:.28rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}.offer-tenant-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-tenant-card__arrow--expanded .offer-tenant-card__icon{transform:rotate(180deg)}.offer-tenant-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-tenant-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"], dependencies: [{ kind: "component", type: OfferTenantExpanded, selector: "rolatech-offer-tenant-expanded", inputs: ["tenant"] }] });
|
|
1417
1434
|
}
|
|
1418
1435
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantCard, decorators: [{
|
|
1419
1436
|
type: Component,
|
|
1420
|
-
args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], host: { class: 'block' }, template: "<div class=\"
|
|
1437
|
+
args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], host: { class: 'block' }, template: "<div class=\"offer-tenant-card\">\n <button\n type=\"button\"\n class=\"offer-tenant-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-tenant-card__copy\">\n <div class=\"offer-tenant-card__topline\">Applicant {{ index() + 1 }}</div>\n <div class=\"offer-tenant-card__name\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"offer-tenant-card__contact\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"offer-tenant-card__chips\">\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"offer-tenant-card__chip\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"offer-tenant-card__arrow\" [class.offer-tenant-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-tenant-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-tenant-card__body\">\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-tenant-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-tenant-card__toggle{display:flex;width:100%;padding:1rem;gap:.9rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-tenant-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-tenant-card__copy{display:flex;flex:1;min-width:0;flex-direction:column;gap:.35rem}.offer-tenant-card__topline{color:var(--rt-brand-color);font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em}.offer-tenant-card__name{color:var(--rt-text-primary);font-size:1rem;font-weight:700;line-height:1.3}.offer-tenant-card__contact{color:var(--rt-text-secondary);font-size:.82rem;line-height:1.5}.offer-tenant-card__chips{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.2rem}.offer-tenant-card__chip{display:inline-flex;align-items:center;min-height:1.85rem;padding:.28rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}.offer-tenant-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-tenant-card__arrow--expanded .offer-tenant-card__icon{transform:rotate(180deg)}.offer-tenant-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-tenant-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"] }]
|
|
1421
1438
|
}], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: true }] }], tenant: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenant", required: true }] }], onToggleInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "onToggleInput", required: true }] }] } });
|
|
1422
1439
|
|
|
1423
1440
|
class OfferTenantsAccordion {
|
|
@@ -1449,11 +1466,11 @@ class OfferTenantsAccordion {
|
|
|
1449
1466
|
this.expandedIndexes.set(new Set());
|
|
1450
1467
|
}
|
|
1451
1468
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1452
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"
|
|
1469
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"offer-tenants-accordion\">\n <div class=\"offer-tenants-accordion__head\">\n <div class=\"offer-tenants-accordion__copy\">\n <span class=\"offer-tenants-accordion__eyebrow\">Applicants</span>\n <h3 class=\"offer-tenants-accordion__title\">Tenant profiles</h3>\n <p class=\"offer-tenants-accordion__description\">\n Review identity, affordability, and guarantor information for each applicant attached to the offer.\n </p>\n </div>\n\n <div class=\"offer-tenants-accordion__tools\">\n <span class=\"offer-tenants-accordion__count\">{{ ts.length }} applicants</span>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-tenants-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-tenants-accordion__empty\">No applicants have been attached to this offer yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-tenants-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-tenants-accordion__head,.offer-tenants-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-tenants-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-tenants-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-tenants-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-tenants-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-tenants-accordion__tool,.offer-tenants-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-tenants-accordion__tool{cursor:pointer;transition:border-color .2s ease,background .2s ease,color .2s ease}.offer-tenants-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-tenants-accordion__tool:disabled{cursor:not-allowed;opacity:.55}.offer-tenants-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-tenants-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-tenants-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"], dependencies: [{ kind: "component", type: OfferTenantCard, selector: "rolatech-offer-tenant-card", inputs: ["index", "expanded", "tenant", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1453
1470
|
}
|
|
1454
1471
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferTenantsAccordion, decorators: [{
|
|
1455
1472
|
type: Component,
|
|
1456
|
-
args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (tenants(); as ts) {\n<section class=\"
|
|
1473
|
+
args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (tenants(); as ts) {\n<section class=\"offer-tenants-accordion\">\n <div class=\"offer-tenants-accordion__head\">\n <div class=\"offer-tenants-accordion__copy\">\n <span class=\"offer-tenants-accordion__eyebrow\">Applicants</span>\n <h3 class=\"offer-tenants-accordion__title\">Tenant profiles</h3>\n <p class=\"offer-tenants-accordion__description\">\n Review identity, affordability, and guarantor information for each applicant attached to the offer.\n </p>\n </div>\n\n <div class=\"offer-tenants-accordion__tools\">\n <span class=\"offer-tenants-accordion__count\">{{ ts.length }} applicants</span>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-tenants-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-tenants-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-tenants-accordion__empty\">No applicants have been attached to this offer yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-tenants-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-tenants-accordion__head,.offer-tenants-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-tenants-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-tenants-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-tenants-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-tenants-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-tenants-accordion__tool,.offer-tenants-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-tenants-accordion__tool{cursor:pointer;transition:border-color .2s ease,background .2s ease,color .2s ease}.offer-tenants-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-tenants-accordion__tool:disabled{cursor:not-allowed;opacity:.55}.offer-tenants-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-tenants-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-tenants-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"] }]
|
|
1457
1474
|
}] });
|
|
1458
1475
|
|
|
1459
1476
|
class OfferItemCard {
|
|
@@ -1463,21 +1480,31 @@ class OfferItemCard {
|
|
|
1463
1480
|
return m?.url ?? null;
|
|
1464
1481
|
}
|
|
1465
1482
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1466
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1483
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-item-card\">\n <div class=\"offer-item-card__head\">\n <div class=\"offer-item-card__copy\">\n <span class=\"offer-item-card__eyebrow\">Property snapshot</span>\n <h3 class=\"offer-item-card__title\">{{ o.item.title }}</h3>\n </div>\n <span class=\"offer-item-card__property-chip\">Property ID {{ o.item.propertyId }}</span>\n </div>\n\n <div class=\"offer-item-card__content\">\n @if (firstMediaUrl(o); as mediaUrl) {\n <img class=\"offer-item-card__media\" [src]=\"mediaUrl\" [alt]=\"o.item.title\" />\n } @else {\n <div class=\"offer-item-card__placeholder\">Property image unavailable</div>\n }\n\n <div class=\"offer-item-card__panel\">\n <div class=\"offer-item-card__price\">{{ o.item.amount | price }}</div>\n\n <div class=\"offer-item-card__facts\">\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bedrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bedrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bathrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bathrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Receptions</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.receptions }}</strong>\n </article>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-item-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:var(--rt-base-background, #ffffff);box-shadow:0 18px 38px -34px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.offer-item-card__head,.offer-item-card__copy{display:flex;flex-direction:column;gap:.45rem}.offer-item-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.3rem .64rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-item-card__title{margin:0;color:var(--rt-text-primary);font-size:1.2rem;line-height:1.2;font-weight:750}.offer-item-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-item-card__property-chip{display:inline-flex;align-self:flex-start;min-height:2rem;padding:.38rem .7rem;border-radius:999px;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-item-card__content{display:grid;gap:1rem}.offer-item-card__media,.offer-item-card__placeholder{width:100%;min-height:15rem;border-radius:1.15rem}.offer-item-card__media{object-fit:cover;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent)}.offer-item-card__placeholder{display:grid;place-items:center;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 86%,var(--rt-brand-color) 14%));color:var(--rt-text-secondary);text-align:center;padding:1rem}.offer-item-card__panel{display:flex;flex-direction:column;gap:1rem}.offer-item-card__price{color:var(--rt-text-primary);font-size:clamp(1.5rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-item-card__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.85rem}.offer-item-card__fact{display:flex;flex-direction:column;gap:.2rem;padding:.85rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-item-card__fact-label{color:var(--rt-text-secondary);font-size:.76rem}.offer-item-card__fact-value{color:var(--rt-text-primary);font-size:1rem}@media(min-width:960px){.offer-item-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-item-card__content{grid-template-columns:minmax(0,1.1fr) minmax(18rem,.95fr);align-items:start}.offer-item-card__media{min-height:18rem}}\n"], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
1467
1484
|
}
|
|
1468
1485
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferItemCard, decorators: [{
|
|
1469
1486
|
type: Component,
|
|
1470
|
-
args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1487
|
+
args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-item-card\">\n <div class=\"offer-item-card__head\">\n <div class=\"offer-item-card__copy\">\n <span class=\"offer-item-card__eyebrow\">Property snapshot</span>\n <h3 class=\"offer-item-card__title\">{{ o.item.title }}</h3>\n </div>\n <span class=\"offer-item-card__property-chip\">Property ID {{ o.item.propertyId }}</span>\n </div>\n\n <div class=\"offer-item-card__content\">\n @if (firstMediaUrl(o); as mediaUrl) {\n <img class=\"offer-item-card__media\" [src]=\"mediaUrl\" [alt]=\"o.item.title\" />\n } @else {\n <div class=\"offer-item-card__placeholder\">Property image unavailable</div>\n }\n\n <div class=\"offer-item-card__panel\">\n <div class=\"offer-item-card__price\">{{ o.item.amount | price }}</div>\n\n <div class=\"offer-item-card__facts\">\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bedrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bedrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Bathrooms</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.bathrooms }}</strong>\n </article>\n <article class=\"offer-item-card__fact\">\n <span class=\"offer-item-card__fact-label\">Receptions</span>\n <strong class=\"offer-item-card__fact-value\">{{ o.item.receptions }}</strong>\n </article>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-item-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:var(--rt-base-background, #ffffff);box-shadow:0 18px 38px -34px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.offer-item-card__head,.offer-item-card__copy{display:flex;flex-direction:column;gap:.45rem}.offer-item-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.3rem .64rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-item-card__title{margin:0;color:var(--rt-text-primary);font-size:1.2rem;line-height:1.2;font-weight:750}.offer-item-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-item-card__property-chip{display:inline-flex;align-self:flex-start;min-height:2rem;padding:.38rem .7rem;border-radius:999px;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-item-card__content{display:grid;gap:1rem}.offer-item-card__media,.offer-item-card__placeholder{width:100%;min-height:15rem;border-radius:1.15rem}.offer-item-card__media{object-fit:cover;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent)}.offer-item-card__placeholder{display:grid;place-items:center;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 86%,var(--rt-brand-color) 14%));color:var(--rt-text-secondary);text-align:center;padding:1rem}.offer-item-card__panel{display:flex;flex-direction:column;gap:1rem}.offer-item-card__price{color:var(--rt-text-primary);font-size:clamp(1.5rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-item-card__facts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.85rem}.offer-item-card__fact{display:flex;flex-direction:column;gap:.2rem;padding:.85rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-item-card__fact-label{color:var(--rt-text-secondary);font-size:.76rem}.offer-item-card__fact-value{color:var(--rt-text-primary);font-size:1rem}@media(min-width:960px){.offer-item-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-item-card__content{grid-template-columns:minmax(0,1.1fr) minmax(18rem,.95fr);align-items:start}.offer-item-card__media{min-height:18rem}}\n"] }]
|
|
1471
1488
|
}] });
|
|
1472
1489
|
|
|
1473
1490
|
class OfferSaleDetailsCard {
|
|
1474
1491
|
ctx = inject(OfferDetailContext);
|
|
1492
|
+
humanize(value) {
|
|
1493
|
+
if (!value) {
|
|
1494
|
+
return '—';
|
|
1495
|
+
}
|
|
1496
|
+
return value
|
|
1497
|
+
.trim()
|
|
1498
|
+
.replace(/[_-]+/g, ' ')
|
|
1499
|
+
.toLowerCase()
|
|
1500
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1501
|
+
}
|
|
1475
1502
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1476
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1503
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-sale-details-card\">\n <div class=\"offer-sale-details-card__head\">\n <div>\n <span class=\"offer-sale-details-card__eyebrow\">Buyer terms</span>\n <h3 class=\"offer-sale-details-card__title\">Sale offer details</h3>\n <p class=\"offer-sale-details-card__description\">Buyer details and funding information attached to the submitted sale offer.</p>\n </div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"offer-sale-details-card__grid\">\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Offer amount</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.amount | price }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Payment method</span>\n <strong class=\"offer-sale-details-card__value\">{{ humanize(s.paymentMethod) }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Buyer name</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.buyerName ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Phone</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.phone ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Email</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.email ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Exchange target</span>\n <strong class=\"offer-sale-details-card__value\">{{\n s.proposedExchangeDate ? (s.proposedExchangeDate | date: 'mediumDate') : '\u2014'\n }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field offer-sale-details-card__field--wide\">\n <span class=\"offer-sale-details-card__label\">Solicitor company</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.solicitorCompanyName ?? '\u2014' }}</strong>\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-sale-details-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-sale-details-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-sale-details-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-sale-details-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-sale-details-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-sale-details-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-sale-details-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-sale-details-card__field--wide{grid-column:1/-1}.offer-sale-details-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-sale-details-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
1477
1504
|
}
|
|
1478
1505
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSaleDetailsCard, decorators: [{
|
|
1479
1506
|
type: Component,
|
|
1480
|
-
args: [{ selector: 'rolatech-offer-sale-details-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
1507
|
+
args: [{ selector: 'rolatech-offer-sale-details-card', imports: [CommonModule, PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-sale-details-card\">\n <div class=\"offer-sale-details-card__head\">\n <div>\n <span class=\"offer-sale-details-card__eyebrow\">Buyer terms</span>\n <h3 class=\"offer-sale-details-card__title\">Sale offer details</h3>\n <p class=\"offer-sale-details-card__description\">Buyer details and funding information attached to the submitted sale offer.</p>\n </div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"offer-sale-details-card__grid\">\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Offer amount</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.amount | price }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Payment method</span>\n <strong class=\"offer-sale-details-card__value\">{{ humanize(s.paymentMethod) }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Buyer name</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.buyerName ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Phone</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.phone ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Email</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.email ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field\">\n <span class=\"offer-sale-details-card__label\">Exchange target</span>\n <strong class=\"offer-sale-details-card__value\">{{\n s.proposedExchangeDate ? (s.proposedExchangeDate | date: 'mediumDate') : '\u2014'\n }}</strong>\n </article>\n <article class=\"offer-sale-details-card__field offer-sale-details-card__field--wide\">\n <span class=\"offer-sale-details-card__label\">Solicitor company</span>\n <strong class=\"offer-sale-details-card__value\">{{ s.solicitorCompanyName ?? '\u2014' }}</strong>\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-sale-details-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-sale-details-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-sale-details-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-sale-details-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-sale-details-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-sale-details-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-sale-details-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.9rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.offer-sale-details-card__field--wide{grid-column:1/-1}.offer-sale-details-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-sale-details-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}\n"] }]
|
|
1481
1508
|
}] });
|
|
1482
1509
|
|
|
1483
1510
|
// offer-manage.facade.ts
|
|
@@ -1879,11 +1906,11 @@ class OfferHistoryCard {
|
|
|
1879
1906
|
return `${this.actorLabel(h.actor)} • ${this.actionLabel(h.action)}`;
|
|
1880
1907
|
}
|
|
1881
1908
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1882
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryCard, isStandalone: true, selector: "rolatech-offer-history-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, history: { classPropertyName: "history", publicName: "history", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"
|
|
1909
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryCard, isStandalone: true, selector: "rolatech-offer-history-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, history: { classPropertyName: "history", publicName: "history", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"offer-history-card\">\n <button\n type=\"button\"\n class=\"offer-history-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-history-card__copy\">\n <div class=\"offer-history-card__meta\">\n <span class=\"offer-history-card__actor\">{{ actorLabel(history().actor) }}</span>\n <span class=\"offer-history-card__timestamp\">{{ formatTs(history().timestamp) }}</span>\n </div>\n <div class=\"offer-history-card__summary\">{{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"offer-history-card__arrow\" [class.offer-history-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-history-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-history-card__body\">\n <rolatech-offer-history-expanded [history]=\"history()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-history-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-history-card__toggle{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.9rem;padding:1rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-history-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-history-card__copy{display:flex;min-width:0;flex:1;flex-direction:column;gap:.35rem}.offer-history-card__meta{display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}.offer-history-card__actor{display:inline-flex;align-items:center;min-height:1.8rem;padding:.26rem .58rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700}.offer-history-card__timestamp{color:var(--rt-text-secondary);font-size:.8rem}.offer-history-card__summary{color:var(--rt-text-primary);font-size:.98rem;font-weight:700;line-height:1.35}.offer-history-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-history-card__arrow--expanded .offer-history-card__icon{transform:rotate(180deg)}.offer-history-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-history-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferHistoryExpanded, selector: "rolatech-offer-history-expanded", inputs: ["history"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1883
1910
|
}
|
|
1884
1911
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryCard, decorators: [{
|
|
1885
1912
|
type: Component,
|
|
1886
|
-
args: [{ selector: 'rolatech-offer-history-card', imports: [CommonModule, OfferHistoryExpanded], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "<div class=\"
|
|
1913
|
+
args: [{ selector: 'rolatech-offer-history-card', imports: [CommonModule, OfferHistoryExpanded], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "<div class=\"offer-history-card\">\n <button\n type=\"button\"\n class=\"offer-history-card__toggle\"\n (click)=\"onToggle()\"\n >\n <div class=\"offer-history-card__copy\">\n <div class=\"offer-history-card__meta\">\n <span class=\"offer-history-card__actor\">{{ actorLabel(history().actor) }}</span>\n <span class=\"offer-history-card__timestamp\">{{ formatTs(history().timestamp) }}</span>\n </div>\n <div class=\"offer-history-card__summary\">{{ actionLabel(history().action) }}</div>\n </div>\n\n <div class=\"offer-history-card__arrow\" [class.offer-history-card__arrow--expanded]=\"expanded()\">\n <svg class=\"offer-history-card__icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <div class=\"offer-history-card__body\">\n <rolatech-offer-history-expanded [history]=\"history()\" />\n </div>\n }\n</div>\n", styles: [":host{display:block}.offer-history-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent);overflow:hidden}.offer-history-card__toggle{display:flex;width:100%;align-items:center;justify-content:space-between;gap:.9rem;padding:1rem;border:0;background:transparent;text-align:left;cursor:pointer;transition:background .2s ease}.offer-history-card__toggle:hover{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-history-card__copy{display:flex;min-width:0;flex:1;flex-direction:column;gap:.35rem}.offer-history-card__meta{display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}.offer-history-card__actor{display:inline-flex;align-items:center;min-height:1.8rem;padding:.26rem .58rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700}.offer-history-card__timestamp{color:var(--rt-text-secondary);font-size:.8rem}.offer-history-card__summary{color:var(--rt-text-primary);font-size:.98rem;font-weight:700;line-height:1.35}.offer-history-card__arrow{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);flex-shrink:0}.offer-history-card__arrow--expanded .offer-history-card__icon{transform:rotate(180deg)}.offer-history-card__icon{width:1rem;height:1rem;transition:transform .2s ease}.offer-history-card__body{border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}\n"] }]
|
|
1887
1914
|
}], propDecorators: { index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: true }] }], history: [{ type: i0.Input, args: [{ isSignal: true, alias: "history", required: true }] }], onToggleInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "onToggleInput", required: true }] }] } });
|
|
1888
1915
|
|
|
1889
1916
|
class OfferHistoryAccordion {
|
|
@@ -1927,14 +1954,14 @@ class OfferHistoryAccordion {
|
|
|
1927
1954
|
}
|
|
1928
1955
|
}
|
|
1929
1956
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1930
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryAccordion, isStandalone: true, selector: "rolatech-offer-history-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (history(); as ts) {\n<section class=\"
|
|
1957
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHistoryAccordion, isStandalone: true, selector: "rolatech-offer-history-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (history(); as ts) {\n<section class=\"offer-history-accordion\">\n <div class=\"offer-history-accordion__head\">\n <div class=\"offer-history-accordion__copy\">\n <span class=\"offer-history-accordion__eyebrow\">Timeline</span>\n <h3 class=\"offer-history-accordion__title\">Offer history</h3>\n <p class=\"offer-history-accordion__description\">\n Every submitted change, view, and response is recorded here so you can audit the full negotiation trail.\n </p>\n </div>\n\n <div class=\"offer-history-accordion__tools\">\n <span class=\"offer-history-accordion__count\">{{ ts.length }} events</span>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-history-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-history-accordion__empty\">No recorded offer events yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-history-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-history-accordion__head,.offer-history-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-history-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-history-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-history-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-history-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-history-accordion__tool,.offer-history-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-history-accordion__tool{cursor:pointer}.offer-history-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-history-accordion__tool:disabled{opacity:.55;cursor:not-allowed}.offer-history-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-history-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-history-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"], dependencies: [{ kind: "component", type: OfferHistoryCard, selector: "rolatech-offer-history-card", inputs: ["index", "expanded", "history", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1931
1958
|
}
|
|
1932
1959
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHistoryAccordion, decorators: [{
|
|
1933
1960
|
type: Component,
|
|
1934
|
-
args: [{ selector: 'rolatech-offer-history-accordion', imports: [OfferHistoryCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (history(); as ts) {\n<section class=\"
|
|
1961
|
+
args: [{ selector: 'rolatech-offer-history-accordion', imports: [OfferHistoryCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (history(); as ts) {\n<section class=\"offer-history-accordion\">\n <div class=\"offer-history-accordion__head\">\n <div class=\"offer-history-accordion__copy\">\n <span class=\"offer-history-accordion__eyebrow\">Timeline</span>\n <h3 class=\"offer-history-accordion__title\">Offer history</h3>\n <p class=\"offer-history-accordion__description\">\n Every submitted change, view, and response is recorded here so you can audit the full negotiation trail.\n </p>\n </div>\n\n <div class=\"offer-history-accordion__tools\">\n <span class=\"offer-history-accordion__count\">{{ ts.length }} events</span>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"offer-history-accordion__tool\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"offer-history-accordion__list\">\n @for (t of ts; track $index) {\n <rolatech-offer-history-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [history]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"offer-history-accordion__empty\">No recorded offer events yet.</div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-history-accordion{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:var(--rt-base-background, #ffffff)}.offer-history-accordion__head,.offer-history-accordion__copy{display:flex;flex-direction:column;gap:.4rem}.offer-history-accordion__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-history-accordion__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:750}.offer-history-accordion__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-history-accordion__tools{display:flex;flex-wrap:wrap;gap:.55rem}.offer-history-accordion__tool,.offer-history-accordion__count{display:inline-flex;align-items:center;min-height:2rem;padding:.36rem .7rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-history-accordion__tool{cursor:pointer}.offer-history-accordion__tool:hover:not(:disabled){border-color:color-mix(in srgb,var(--rt-brand-color) 30%,var(--rt-border-color, rgba(15, 23, 42, .08)));color:var(--rt-brand-color)}.offer-history-accordion__tool:disabled{opacity:.55;cursor:not-allowed}.offer-history-accordion__list{display:flex;flex-direction:column;gap:.85rem}.offer-history-accordion__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;color:var(--rt-text-secondary)}@media(min-width:900px){.offer-history-accordion__head{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}}\n"] }]
|
|
1935
1962
|
}] });
|
|
1936
1963
|
|
|
1937
|
-
const MY_FORMATS$
|
|
1964
|
+
const MY_FORMATS$6 = {
|
|
1938
1965
|
parse: {
|
|
1939
1966
|
dateInput: 'YYYY-MM-DD',
|
|
1940
1967
|
},
|
|
@@ -2016,7 +2043,7 @@ class OfferCounterDialog {
|
|
|
2016
2043
|
useClass: MomentDateAdapter,
|
|
2017
2044
|
deps: [MAT_DATE_LOCALE],
|
|
2018
2045
|
},
|
|
2019
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
2046
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
|
|
2020
2047
|
], ngImport: i0, template: "<div class=\"grid\">\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Offer amount (PCM)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().amount\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Move-in date</mat-label>\n <input matInput [min]=\"minDate\" (focus)=\"dp.open()\" [matDatepicker]=\"dp\" [(ngModel)]=\"term().moveInDate\" readonly />\n <mat-datepicker-toggle matIconSuffix [for]=\"dp\"></mat-datepicker-toggle>\n <mat-datepicker #dp></mat-datepicker>\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Payment frequency</mat-label>\n <mat-select [(ngModel)]=\"term().paymentFrequency\">\n <mat-option value=\"MONTHLY\">Monthly</mat-option>\n <mat-option value=\"QUARTERLY\">Quarterly</mat-option>\n <mat-option value=\"SEMI_ANNUALLY\">Semi-annually</mat-option>\n <mat-option value=\"ANNUALLY\">Annually</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Tenancy length (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().tenancyLengthMonths\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Break clause (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select [(ngModel)]=\"term().furniture\">\n <mat-option value=\"FURNISHED\">Furnished</mat-option>\n <mat-option value=\"UNFURNISHED\">Unfurnished</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\" class=\"w-full\">\n <mat-label>{{ petsInputFocused() ? 'Pets' : 'Add a pet (e.g., Dog, Cat)' }}</mat-label>\n\n <mat-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of (term().pets); track p) {\n <mat-chip-row (removed)=\"removePet(p)\">\n {{ p }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + p\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n\n <input\n matInput\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (focus)=\"petsInputFocused.set(true)\"\n (blur)=\"petsInputFocused.set(false)\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n @if ((term().pets).length <= 0) {\n <mat-error>At least one pet or none is required</mat-error>\n }\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" [(ngModel)]=\"term().smoker\">\n <span class=\"text-lg font-bold\">Smoker</span>\n </mat-slide-toggle>\n\n <mat-form-field class=\"span2\">\n <mat-label>Additional requests</mat-label>\n <textarea matInput rows=\"3\" [(ngModel)]=\"term().additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i5$1.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i7.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i7.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i7.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i7.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
2021
2048
|
}
|
|
2022
2049
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterDialog, decorators: [{
|
|
@@ -2038,7 +2065,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
2038
2065
|
useClass: MomentDateAdapter,
|
|
2039
2066
|
deps: [MAT_DATE_LOCALE],
|
|
2040
2067
|
},
|
|
2041
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
2068
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
|
|
2042
2069
|
], template: "<div class=\"grid\">\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Offer amount (PCM)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().amount\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Move-in date</mat-label>\n <input matInput [min]=\"minDate\" (focus)=\"dp.open()\" [matDatepicker]=\"dp\" [(ngModel)]=\"term().moveInDate\" readonly />\n <mat-datepicker-toggle matIconSuffix [for]=\"dp\"></mat-datepicker-toggle>\n <mat-datepicker #dp></mat-datepicker>\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Payment frequency</mat-label>\n <mat-select [(ngModel)]=\"term().paymentFrequency\">\n <mat-option value=\"MONTHLY\">Monthly</mat-option>\n <mat-option value=\"QUARTERLY\">Quarterly</mat-option>\n <mat-option value=\"SEMI_ANNUALLY\">Semi-annually</mat-option>\n <mat-option value=\"ANNUALLY\">Annually</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <div class=\"grid grid-cols-12 gap-3\">\n <mat-form-field class=\"col-span-4\">\n <mat-label>Tenancy length (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().tenancyLengthMonths\" />\n </mat-form-field>\n\n <mat-form-field class=\"col-span-4\">\n <mat-label>Break clause (months)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"term().breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select [(ngModel)]=\"term().furniture\">\n <mat-option value=\"FURNISHED\">Furnished</mat-option>\n <mat-option value=\"UNFURNISHED\">Unfurnished</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\" class=\"w-full\">\n <mat-label>{{ petsInputFocused() ? 'Pets' : 'Add a pet (e.g., Dog, Cat)' }}</mat-label>\n\n <mat-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of (term().pets); track p) {\n <mat-chip-row (removed)=\"removePet(p)\">\n {{ p }}\n <button matChipRemove [attr.aria-label]=\"'remove ' + p\">\n <mat-icon>cancel</mat-icon>\n </button>\n </mat-chip-row>\n }\n\n <input\n matInput\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (focus)=\"petsInputFocused.set(true)\"\n (blur)=\"petsInputFocused.set(false)\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n @if ((term().pets).length <= 0) {\n <mat-error>At least one pet or none is required</mat-error>\n }\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" [(ngModel)]=\"term().smoker\">\n <span class=\"text-lg font-bold\">Smoker</span>\n </mat-slide-toggle>\n\n <mat-form-field class=\"span2\">\n <mat-label>Additional requests</mat-label>\n <textarea matInput rows=\"3\" [(ngModel)]=\"term().additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n" }]
|
|
2043
2070
|
}], propDecorators: { term: [{ type: i0.Input, args: [{ isSignal: true, alias: "term", required: true }] }, { type: i0.Output, args: ["termChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
|
|
2044
2071
|
|
|
@@ -2861,22 +2888,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
2861
2888
|
|
|
2862
2889
|
class OfferCounterLatestCard {
|
|
2863
2890
|
f = inject(PropertyOfferNegotiationFacade);
|
|
2891
|
+
humanize(value) {
|
|
2892
|
+
if (!value) {
|
|
2893
|
+
return '—';
|
|
2894
|
+
}
|
|
2895
|
+
return value
|
|
2896
|
+
.trim()
|
|
2897
|
+
.replace(/[_-]+/g, ' ')
|
|
2898
|
+
.toLowerCase()
|
|
2899
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
2900
|
+
}
|
|
2901
|
+
boolLabel(value) {
|
|
2902
|
+
if (value === null || value === undefined) {
|
|
2903
|
+
return '—';
|
|
2904
|
+
}
|
|
2905
|
+
return value ? 'Yes' : 'No';
|
|
2906
|
+
}
|
|
2864
2907
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2865
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterLatestCard, isStandalone: true, selector: "rolatech-offer-counter-latest-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.latest(); as rt) {\n<section class=\"
|
|
2908
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterLatestCard, isStandalone: true, selector: "rolatech-offer-counter-latest-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.latest(); as rt) {\n<section class=\"offer-counter-latest-card\">\n <div class=\"offer-counter-latest-card__head\">\n <div>\n <span class=\"offer-counter-latest-card__eyebrow\">Negotiated terms</span>\n <h3 class=\"offer-counter-latest-card__title\">Latest offer version</h3>\n <p class=\"offer-counter-latest-card__description\">This version reflects the newest negotiated rental terms.</p>\n </div>\n\n <div class=\"offer-counter-latest-card__badges\">\n @if (f.editedFields().size) {\n <span class=\"offer-counter-latest-card__badge\">{{ f.editedFields().size }} updated fields</span>\n } @if (f.editMode()) {\n <span class=\"offer-counter-latest-card__badge offer-counter-latest-card__badge--active\">Editing</span>\n }\n </div>\n </div>\n\n @if (f.editMode()) {\n <div class=\"offer-counter-latest-card__editor\">\n <rolatech-offer-counter-edit />\n </div>\n } @else {\n <div class=\"offer-counter-latest-card__grid\">\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Offer amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field offer-counter-latest-card__field--wide\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value offer-counter-latest-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-counter-latest-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 10%,var(--rt-base-background, #ffffff)),color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent))}.offer-counter-latest-card__head{display:flex;flex-direction:column;gap:.75rem}.offer-counter-latest-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:#3b82f61f;color:#1d4ed8;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-latest-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:760}.offer-counter-latest-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-latest-card__badges{display:flex;flex-wrap:wrap;gap:.5rem}.offer-counter-latest-card__badge,.offer-counter-latest-card__edited{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border-radius:999px;font-size:.76rem;font-weight:700}.offer-counter-latest-card__badge{border:1px solid rgba(59,130,246,.18);background:#3b82f61a;color:#1d4ed8}.offer-counter-latest-card__badge--active{border-color:#10b98133;background:#10b9811f;color:#047857}.offer-counter-latest-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-latest-card__field{display:flex;flex-direction:column;gap:.3rem;padding:.92rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.offer-counter-latest-card__field--wide{grid-column:1/-1}.offer-counter-latest-card__label-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.offer-counter-latest-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-latest-card__edited{border:1px solid rgba(249,115,22,.22);background:#f973161a;color:#c2410c}.offer-counter-latest-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-counter-latest-card__value--multiline{white-space:pre-wrap}.offer-counter-latest-card__editor{padding:.25rem 0 0}@media(min-width:960px){.offer-counter-latest-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: OfferCounterEdit, selector: "rolatech-offer-counter-edit" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
2866
2909
|
}
|
|
2867
2910
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterLatestCard, decorators: [{
|
|
2868
2911
|
type: Component,
|
|
2869
|
-
args: [{ selector: 'rolatech-offer-counter-latest-card', imports: [CommonModule, PricePipe, OfferCounterEdit, MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.latest(); as rt) {\n<section class=\"
|
|
2912
|
+
args: [{ selector: 'rolatech-offer-counter-latest-card', imports: [CommonModule, PricePipe, OfferCounterEdit, MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.latest(); as rt) {\n<section class=\"offer-counter-latest-card\">\n <div class=\"offer-counter-latest-card__head\">\n <div>\n <span class=\"offer-counter-latest-card__eyebrow\">Negotiated terms</span>\n <h3 class=\"offer-counter-latest-card__title\">Latest offer version</h3>\n <p class=\"offer-counter-latest-card__description\">This version reflects the newest negotiated rental terms.</p>\n </div>\n\n <div class=\"offer-counter-latest-card__badges\">\n @if (f.editedFields().size) {\n <span class=\"offer-counter-latest-card__badge\">{{ f.editedFields().size }} updated fields</span>\n } @if (f.editMode()) {\n <span class=\"offer-counter-latest-card__badge offer-counter-latest-card__badge--active\">Editing</span>\n }\n </div>\n </div>\n\n @if (f.editMode()) {\n <div class=\"offer-counter-latest-card__editor\">\n <rolatech-offer-counter-edit />\n </div>\n } @else {\n <div class=\"offer-counter-latest-card__grid\">\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Offer amount</span>\n @if (f.isEdited('amount')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Move-in date</span>\n @if (f.isEdited('moveInDate')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Payment frequency</span>\n @if (f.isEdited('paymentFrequency')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Tenancy length</span>\n @if (f.isEdited('tenancyLengthMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Break clause</span>\n @if (f.isEdited('breakClauseMonths')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Pets</span>\n @if (f.isEdited('pets')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Smoker</span>\n @if (f.isEdited('smoker')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Furniture</span>\n @if (f.isEdited('furniture')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-latest-card__field offer-counter-latest-card__field--wide\">\n <div class=\"offer-counter-latest-card__label-row\">\n <span class=\"offer-counter-latest-card__label\">Additional requests</span>\n @if (f.isEdited('additionalRequests')) {\n <span class=\"offer-counter-latest-card__edited\">Edited</span>\n }\n </div>\n <strong class=\"offer-counter-latest-card__value offer-counter-latest-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n }\n</section>\n}\n", styles: [":host{display:block}.offer-counter-latest-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 10%,var(--rt-base-background, #ffffff)),color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent))}.offer-counter-latest-card__head{display:flex;flex-direction:column;gap:.75rem}.offer-counter-latest-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:#3b82f61f;color:#1d4ed8;font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-latest-card__title{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:760}.offer-counter-latest-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-latest-card__badges{display:flex;flex-wrap:wrap;gap:.5rem}.offer-counter-latest-card__badge,.offer-counter-latest-card__edited{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border-radius:999px;font-size:.76rem;font-weight:700}.offer-counter-latest-card__badge{border:1px solid rgba(59,130,246,.18);background:#3b82f61a;color:#1d4ed8}.offer-counter-latest-card__badge--active{border-color:#10b98133;background:#10b9811f;color:#047857}.offer-counter-latest-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-latest-card__field{display:flex;flex-direction:column;gap:.3rem;padding:.92rem .95rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.offer-counter-latest-card__field--wide{grid-column:1/-1}.offer-counter-latest-card__label-row{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.offer-counter-latest-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-latest-card__edited{border:1px solid rgba(249,115,22,.22);background:#f973161a;color:#c2410c}.offer-counter-latest-card__value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.45}.offer-counter-latest-card__value--multiline{white-space:pre-wrap}.offer-counter-latest-card__editor{padding:.25rem 0 0}@media(min-width:960px){.offer-counter-latest-card__head{flex-direction:row;align-items:flex-start;justify-content:space-between}}\n"] }]
|
|
2870
2913
|
}] });
|
|
2871
2914
|
|
|
2872
2915
|
class OfferCounterPreviousCard {
|
|
2873
2916
|
f = inject(PropertyOfferNegotiationFacade);
|
|
2917
|
+
humanize(value) {
|
|
2918
|
+
if (!value) {
|
|
2919
|
+
return '—';
|
|
2920
|
+
}
|
|
2921
|
+
return value
|
|
2922
|
+
.trim()
|
|
2923
|
+
.replace(/[_-]+/g, ' ')
|
|
2924
|
+
.toLowerCase()
|
|
2925
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
2926
|
+
}
|
|
2927
|
+
boolLabel(value) {
|
|
2928
|
+
if (value === null || value === undefined) {
|
|
2929
|
+
return '—';
|
|
2930
|
+
}
|
|
2931
|
+
return value ? 'Yes' : 'No';
|
|
2932
|
+
}
|
|
2874
2933
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2875
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterPreviousCard, isStandalone: true, selector: "rolatech-offer-counter-previous-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.previous(); as rt) {\n<section class=\"
|
|
2934
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferCounterPreviousCard, isStandalone: true, selector: "rolatech-offer-counter-previous-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (f.previous(); as rt) {\n<section class=\"offer-counter-previous-card\">\n <div class=\"offer-counter-previous-card__head\">\n <span class=\"offer-counter-previous-card__eyebrow\">Previous version</span>\n <h3 class=\"offer-counter-previous-card__title\">Earlier rental terms</h3>\n <p class=\"offer-counter-previous-card__description\">\n Use this muted snapshot to compare the previous position against the latest counter.\n </p>\n </div>\n\n <div class=\"offer-counter-previous-card__grid\">\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Offer amount</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Move-in date</span>\n <strong class=\"offer-counter-previous-card__value\"\n >{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong\n >\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Payment frequency</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Tenancy length</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Break clause</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Pets</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Smoker</span>\n <strong class=\"offer-counter-previous-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Furniture</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field offer-counter-previous-card__field--wide\">\n <span class=\"offer-counter-previous-card__label\">Additional requests</span>\n <strong class=\"offer-counter-previous-card__value offer-counter-previous-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-counter-previous-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent);opacity:.82}.offer-counter-previous-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-counter-previous-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-text-secondary) 10%,transparent);color:var(--rt-text-secondary);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-previous-card__title{margin:0;color:var(--rt-text-primary);font-size:1.02rem;font-weight:740}.offer-counter-previous-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-previous-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-previous-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.88rem .94rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,transparent)}.offer-counter-previous-card__field--wide{grid-column:1/-1}.offer-counter-previous-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-previous-card__value{color:var(--rt-text-primary);font-size:.94rem;line-height:1.45}.offer-counter-previous-card__value--multiline{white-space:pre-wrap}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
2876
2935
|
}
|
|
2877
2936
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferCounterPreviousCard, decorators: [{
|
|
2878
2937
|
type: Component,
|
|
2879
|
-
args: [{ selector: 'rolatech-offer-counter-previous-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.previous(); as rt) {\n<section class=\"
|
|
2938
|
+
args: [{ selector: 'rolatech-offer-counter-previous-card', imports: [CommonModule, PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (f.previous(); as rt) {\n<section class=\"offer-counter-previous-card\">\n <div class=\"offer-counter-previous-card__head\">\n <span class=\"offer-counter-previous-card__eyebrow\">Previous version</span>\n <h3 class=\"offer-counter-previous-card__title\">Earlier rental terms</h3>\n <p class=\"offer-counter-previous-card__description\">\n Use this muted snapshot to compare the previous position against the latest counter.\n </p>\n </div>\n\n <div class=\"offer-counter-previous-card__grid\">\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Offer amount</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.amount | price }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Move-in date</span>\n <strong class=\"offer-counter-previous-card__value\"\n >{{ rt.moveInDate ? (rt.moveInDate | date: 'mediumDate') : '\u2014' }}</strong\n >\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Payment frequency</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.paymentFrequency) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Tenancy length</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Break clause</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.breakClauseMonths ?? '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Pets</span>\n <strong class=\"offer-counter-previous-card__value\">{{ rt.pets.length ? rt.pets.join(', ') : '\u2014' }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Smoker</span>\n <strong class=\"offer-counter-previous-card__value\">{{ boolLabel(rt.smoker) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field\">\n <span class=\"offer-counter-previous-card__label\">Furniture</span>\n <strong class=\"offer-counter-previous-card__value\">{{ humanize(rt.furniture) }}</strong>\n </article>\n <article class=\"offer-counter-previous-card__field offer-counter-previous-card__field--wide\">\n <span class=\"offer-counter-previous-card__label\">Additional requests</span>\n <strong class=\"offer-counter-previous-card__value offer-counter-previous-card__value--multiline\"\n >{{ rt.additionalRequests ?? '\u2014' }}</strong\n >\n </article>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-counter-previous-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;padding:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 84%,transparent);opacity:.82}.offer-counter-previous-card__head{display:flex;flex-direction:column;gap:.4rem}.offer-counter-previous-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-text-secondary) 10%,transparent);color:var(--rt-text-secondary);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-counter-previous-card__title{margin:0;color:var(--rt-text-primary);font-size:1.02rem;font-weight:740}.offer-counter-previous-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.6}.offer-counter-previous-card__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-counter-previous-card__field{display:flex;flex-direction:column;gap:.24rem;padding:.88rem .94rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,transparent)}.offer-counter-previous-card__field--wide{grid-column:1/-1}.offer-counter-previous-card__label{color:var(--rt-text-secondary);font-size:.78rem}.offer-counter-previous-card__value{color:var(--rt-text-primary);font-size:.94rem;line-height:1.45}.offer-counter-previous-card__value--multiline{white-space:pre-wrap}\n"] }]
|
|
2880
2939
|
}] });
|
|
2881
2940
|
|
|
2882
2941
|
class OfferNegotiationSection {
|
|
@@ -2912,11 +2971,11 @@ class OfferNegotiationSection {
|
|
|
2912
2971
|
});
|
|
2913
2972
|
}
|
|
2914
2973
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2915
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferNegotiationSection, isStandalone: true, selector: "rolatech-offer-negotiation-section", host: { classAttribute: "block" }, ngImport: i0, template: "@if (!isTerminal()) {\n<section class=\"
|
|
2974
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferNegotiationSection, isStandalone: true, selector: "rolatech-offer-negotiation-section", host: { classAttribute: "block" }, ngImport: i0, template: "@if (!isTerminal()) {\n<section class=\"offer-negotiation-section\">\n <header class=\"offer-negotiation-section__header\">\n <div class=\"offer-negotiation-section__copy\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation controls</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">Respond to the latest offer</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n Keep the negotiation moving with one clear next action. Every response stays recorded in the offer history below.\n </p>\n </div>\n\n @if (f.lock(); as lk) {\n @if (lk.locked) {\n <div class=\"offer-negotiation-section__lock\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n }\n }\n </header>\n\n @if (f.error(); as err) {\n <div class=\"offer-negotiation-section__alert\">{{ err }}</div>\n }\n\n @if (f.editMode()) {\n <div class=\"offer-negotiation-section__edit-note\">\n Counter editing is active below. Review the updated terms, then send the counter-offer when ready.\n </div>\n }\n\n <div class=\"offer-negotiation-section__actions\">\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) {\n Working...\n } @else {\n {{ f.withdrawLabel() }}\n }\n </button>\n }\n\n <span class=\"offer-negotiation-section__spacer\"></span>\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) {\n Working...\n } @else {\n Cancel\n }\n </button>\n </div>\n\n @if (f.showLifecycleActions()) {\n <div class=\"offer-negotiation-section__secondary\">\n <div class=\"offer-negotiation-section__secondary-copy\">\n <span class=\"offer-negotiation-section__secondary-eyebrow\">Workflow steps</span>\n <h4 class=\"offer-negotiation-section__secondary-title\">Progress the deal after negotiation</h4>\n </div>\n\n <div class=\"offer-negotiation-section__secondary-actions\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n} @else {\n <section class=\"offer-negotiation-section offer-negotiation-section--terminal\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation closed</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">This offer is no longer awaiting action</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n The final state has been recorded. Use the history section below to review how the negotiation reached this outcome.\n </p>\n </section>\n}\n", styles: [":host{display:block}.offer-negotiation-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%);box-shadow:0 18px 42px -38px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.offer-negotiation-section__header,.offer-negotiation-section__copy,.offer-negotiation-section__secondary-copy{display:flex;flex-direction:column;gap:.45rem}.offer-negotiation-section__eyebrow,.offer-negotiation-section__secondary-eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .68rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-negotiation-section__heading{display:flex;flex-direction:column;gap:.75rem}.offer-negotiation-section__title,.offer-negotiation-section__secondary-title{margin:0;color:var(--rt-text-primary);font-size:1.08rem;line-height:1.2;font-weight:700}.offer-negotiation-section__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-negotiation-section__status,.offer-negotiation-section__lock{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.1rem;padding:.38rem .75rem;border-radius:999px;font-size:.8rem;font-weight:700}.offer-negotiation-section__status{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-raised-background, #ffffff);color:var(--rt-text-primary)}.offer-negotiation-section__lock{border:1px solid rgba(245,158,11,.24);background:#f59e0b1f;color:#a16207}.offer-negotiation-section__alert,.offer-negotiation-section__edit-note{padding:.85rem .95rem;border-radius:1rem;font-size:.9rem;line-height:1.55}.offer-negotiation-section__alert{border:1px solid rgba(239,68,68,.2);background:#fef2f2e6;color:#b91c1c}.offer-negotiation-section__edit-note{border:1px solid rgba(59,130,246,.16);background:#eff6ffe6;color:#1d4ed8}.offer-negotiation-section__actions,.offer-negotiation-section__secondary-actions{display:flex;flex-wrap:wrap;gap:.7rem}.offer-negotiation-section__spacer{flex:1 1 auto}.offer-negotiation-section__secondary{display:flex;flex-direction:column;gap:.85rem;padding-top:1rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-negotiation-section--terminal{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent)}@media(min-width:900px){.offer-negotiation-section__header{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-negotiation-section__heading{flex-direction:row;align-items:center;justify-content:space-between}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
2916
2975
|
}
|
|
2917
2976
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferNegotiationSection, decorators: [{
|
|
2918
2977
|
type: Component,
|
|
2919
|
-
args: [{ selector: 'rolatech-offer-negotiation-section', imports: [MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (!isTerminal()) {\n<section class=\"
|
|
2978
|
+
args: [{ selector: 'rolatech-offer-negotiation-section', imports: [MatButtonModule], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (!isTerminal()) {\n<section class=\"offer-negotiation-section\">\n <header class=\"offer-negotiation-section__header\">\n <div class=\"offer-negotiation-section__copy\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation controls</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">Respond to the latest offer</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n Keep the negotiation moving with one clear next action. Every response stays recorded in the offer history below.\n </p>\n </div>\n\n @if (f.lock(); as lk) {\n @if (lk.locked) {\n <div class=\"offer-negotiation-section__lock\">Locked by {{ lk.lockedByName ?? lk.lockedById }}</div>\n }\n }\n </header>\n\n @if (f.error(); as err) {\n <div class=\"offer-negotiation-section__alert\">{{ err }}</div>\n }\n\n @if (f.editMode()) {\n <div class=\"offer-negotiation-section__edit-note\">\n Counter editing is active below. Review the updated terms, then send the counter-offer when ready.\n </div>\n }\n\n <div class=\"offer-negotiation-section__actions\">\n <button mat-stroked-button [disabled]=\"!f.canCounter() || f.editMode()\" (click)=\"f.enterEdit()\">Counter</button>\n\n @if (f.canWithdraw()) {\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canWithdraw() || f.withdrawing()\" (click)=\"f.withdraw()\">\n @if (f.withdrawing()) {\n Working...\n } @else {\n {{ f.withdrawLabel() }}\n }\n </button>\n }\n\n <span class=\"offer-negotiation-section__spacer\"></span>\n\n <button mat-flat-button color=\"primary\" [disabled]=\"!f.canAccept() || f.loading()\" (click)=\"f.accept()\">Accept</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canReject() || f.loading()\" (click)=\"reject()\">Reject</button>\n <button mat-stroked-button color=\"warn\" [disabled]=\"!f.canCancel() || f.cancelling()\" (click)=\"cancelOffer()\">\n @if (f.cancelling()) {\n Working...\n } @else {\n Cancel\n }\n </button>\n </div>\n\n @if (f.showLifecycleActions()) {\n <div class=\"offer-negotiation-section__secondary\">\n <div class=\"offer-negotiation-section__secondary-copy\">\n <span class=\"offer-negotiation-section__secondary-eyebrow\">Workflow steps</span>\n <h4 class=\"offer-negotiation-section__secondary-title\">Progress the deal after negotiation</h4>\n </div>\n\n <div class=\"offer-negotiation-section__secondary-actions\">\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkUnderOffer ?? false) || f.loading()\"\n (click)=\"f.markUnderOffer()\"\n >\n Mark under offer\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canStartReferencing ?? false) || f.loading()\"\n (click)=\"f.startReferencing()\"\n >\n Start referencing\n </button>\n <button\n mat-stroked-button\n [disabled]=\"!(f.permissions()?.canMarkReferencesPassed ?? false) || f.loading()\"\n (click)=\"f.markReferencesPassed()\"\n >\n References passed\n </button>\n <button\n mat-stroked-button\n color=\"warn\"\n [disabled]=\"!(f.permissions()?.canMarkReferencesFailed ?? false) || f.loading()\"\n (click)=\"openReferenceFailedDialog()\"\n >\n References failed\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n [disabled]=\"!(f.permissions()?.canMarkCompleted ?? false) || f.loading()\"\n (click)=\"f.markCompleted()\"\n >\n Mark completed\n </button>\n </div>\n </div>\n }\n</section>\n} @else {\n <section class=\"offer-negotiation-section offer-negotiation-section--terminal\">\n <span class=\"offer-negotiation-section__eyebrow\">Negotiation closed</span>\n <div class=\"offer-negotiation-section__heading\">\n <h3 class=\"offer-negotiation-section__title\">This offer is no longer awaiting action</h3>\n <span class=\"offer-negotiation-section__status\">{{ statusLabel() }}</span>\n </div>\n <p class=\"offer-negotiation-section__description\">\n The final state has been recorded. Use the history section below to review how the negotiation reached this outcome.\n </p>\n </section>\n}\n", styles: [":host{display:block}.offer-negotiation-section{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.1rem;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%);box-shadow:0 18px 42px -38px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.offer-negotiation-section__header,.offer-negotiation-section__copy,.offer-negotiation-section__secondary-copy{display:flex;flex-direction:column;gap:.45rem}.offer-negotiation-section__eyebrow,.offer-negotiation-section__secondary-eyebrow{display:inline-flex;align-self:flex-start;padding:.32rem .68rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-negotiation-section__heading{display:flex;flex-direction:column;gap:.75rem}.offer-negotiation-section__title,.offer-negotiation-section__secondary-title{margin:0;color:var(--rt-text-primary);font-size:1.08rem;line-height:1.2;font-weight:700}.offer-negotiation-section__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-negotiation-section__status,.offer-negotiation-section__lock{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.1rem;padding:.38rem .75rem;border-radius:999px;font-size:.8rem;font-weight:700}.offer-negotiation-section__status{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-raised-background, #ffffff);color:var(--rt-text-primary)}.offer-negotiation-section__lock{border:1px solid rgba(245,158,11,.24);background:#f59e0b1f;color:#a16207}.offer-negotiation-section__alert,.offer-negotiation-section__edit-note{padding:.85rem .95rem;border-radius:1rem;font-size:.9rem;line-height:1.55}.offer-negotiation-section__alert{border:1px solid rgba(239,68,68,.2);background:#fef2f2e6;color:#b91c1c}.offer-negotiation-section__edit-note{border:1px solid rgba(59,130,246,.16);background:#eff6ffe6;color:#1d4ed8}.offer-negotiation-section__actions,.offer-negotiation-section__secondary-actions{display:flex;flex-wrap:wrap;gap:.7rem}.offer-negotiation-section__spacer{flex:1 1 auto}.offer-negotiation-section__secondary{display:flex;flex-direction:column;gap:.85rem;padding-top:1rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-negotiation-section--terminal{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent)}@media(min-width:900px){.offer-negotiation-section__header{flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem}.offer-negotiation-section__heading{flex-direction:row;align-items:center;justify-content:space-between}}\n"] }]
|
|
2920
2979
|
}] });
|
|
2921
2980
|
|
|
2922
2981
|
class PropertyManageOfferDetailComponent extends BaseComponent {
|
|
@@ -3278,7 +3337,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
3278
3337
|
}]
|
|
3279
3338
|
}] });
|
|
3280
3339
|
|
|
3281
|
-
const MY_FORMATS$
|
|
3340
|
+
const MY_FORMATS$5 = {
|
|
3282
3341
|
parse: {
|
|
3283
3342
|
dateInput: 'YYYY-MM-DD',
|
|
3284
3343
|
},
|
|
@@ -3394,7 +3453,7 @@ class PropertyFilterBar {
|
|
|
3394
3453
|
useClass: MomentDateAdapter,
|
|
3395
3454
|
deps: [MAT_DATE_LOCALE],
|
|
3396
3455
|
},
|
|
3397
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
3456
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
|
|
3398
3457
|
], ngImport: i0, template: "<div class=\"px-2 mb-3 close\" [class.open]=\"show()\">\n <div class=\"flex flex-col h-min justify-center items-center gap-3\">\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Market</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Market\" [(ngModel)]=\"options().market\">\n @for (m of market; track m) {\n <mat-option [value]=\"m\"> {{ m }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Type</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Type\" [(ngModel)]=\"options().type\">\n @for (type of types | keyvalue; track type) {\n <mat-option [value]=\"type.key\"> {{ type.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Town</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"options().town\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\"> {{ town }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Available date</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\" class=\"z-[1000]\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"options().availableFrom\"\n (dateInput)=\"options().availableFrom = $event.value.format('YYYY-MM-DD')\"\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker panelClass=\"datepicker-on-top\" #startDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/3 overflow-hidden\">\n <div class=\"py-1\">Price range (\u00A3)</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().minPrice\">\n @for (opt of minOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().maxPrice\">\n @for (opt of maxOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/3\">\n <div class=\"py-1\">No. of bedrooms</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min</mat-label>\n <mat-select [(ngModel)]=\"options().minBedrooms\">\n @for (opt of minBedroom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max</mat-label>\n <mat-select [(ngModel)]=\"options().maxBedrooms\">\n @for (opt of maxBedRoom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"h-14 md:w-1/3 self-end w-full\">\n <button mat-flat-button (click)=\"onSearch()\" class=\"w-full min-h-14\">\n <a i18n>Search</a>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}.close{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.open{max-height:1000px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }], encapsulation: i0.ViewEncapsulation.None });
|
|
3399
3458
|
}
|
|
3400
3459
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyFilterBar, decorators: [{
|
|
@@ -3414,7 +3473,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
3414
3473
|
useClass: MomentDateAdapter,
|
|
3415
3474
|
deps: [MAT_DATE_LOCALE],
|
|
3416
3475
|
},
|
|
3417
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
3476
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
|
|
3418
3477
|
], encapsulation: ViewEncapsulation.None, template: "<div class=\"px-2 mb-3 close\" [class.open]=\"show()\">\n <div class=\"flex flex-col h-min justify-center items-center gap-3\">\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Market</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Market\" [(ngModel)]=\"options().market\">\n @for (m of market; track m) {\n <mat-option [value]=\"m\"> {{ m }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Type</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Type\" [(ngModel)]=\"options().type\">\n @for (type of types | keyvalue; track type) {\n <mat-option [value]=\"type.key\"> {{ type.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Town</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"options().town\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\"> {{ town }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/4\">\n <div class=\"py-1\">Available date</div>\n <div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\" class=\"z-[1000]\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"options().availableFrom\"\n (dateInput)=\"options().availableFrom = $event.value.format('YYYY-MM-DD')\"\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker panelClass=\"datepicker-on-top\" #startDatePicker></mat-datepicker>\n </mat-form-field>\n </div>\n </div>\n </div>\n <div class=\"flex flex-col md:flex-row gap-3 w-full items-center\">\n <div class=\"flex flex-col w-full md:w-1/3 overflow-hidden\">\n <div class=\"py-1\">Price range (\u00A3)</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().minPrice\">\n @for (opt of minOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max (PCM)</mat-label>\n <mat-select [(ngModel)]=\"options().maxPrice\">\n @for (opt of maxOptions(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"flex flex-col w-full md:w-1/3\">\n <div class=\"py-1\">No. of bedrooms</div>\n <div class=\"flex flex-row items-center\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Min</mat-label>\n <mat-select [(ngModel)]=\"options().minBedrooms\">\n @for (opt of minBedroom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div class=\"px-2\">-</div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Max</mat-label>\n <mat-select [(ngModel)]=\"options().maxBedrooms\">\n @for (opt of maxBedRoom(); track opt.value) {\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n </div>\n <div class=\"h-14 md:w-1/3 self-end w-full\">\n <button mat-flat-button (click)=\"onSearch()\" class=\"w-full min-h-14\">\n <a i18n>Search</a>\n </button>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}.close{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.open{max-height:1000px}\n"] }]
|
|
3419
3478
|
}], ctorParameters: () => [], propDecorators: { search: [{ type: i0.Output, args: ["search"] }], reset: [{ type: i0.Output, args: ["reset"] }], show: [{ type: i0.Input, args: [{ isSignal: true, alias: "show", required: false }] }, { type: i0.Output, args: ["showChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }, { type: i0.Output, args: ["optionsChange"] }] } });
|
|
3420
3479
|
|
|
@@ -4087,7 +4146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4087
4146
|
], template: "<rolatech-container>\n <div class=\"flex flex-col w-full\">\n @if (loading()) {\n <rolatech-property-media-skeleton></rolatech-property-media-skeleton>\n\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <rolatech-property-info-skeleton class=\"w-full md:w-2/3\"></rolatech-property-info-skeleton>\n <rolatech-property-agent-skeleton class=\"w-full md:w-1/3\"></rolatech-property-agent-skeleton>\n </div>\n } @else {\n @if (property(); as property) {\n <rolatech-property-media [media]=\"property.media ? property.media : []\"></rolatech-property-media>\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <div class=\"w-full md:w-2/3\">\n <rolatech-property-info [property]=\"property\" (wish)=\"onWish($event)\" [inWishList]=\"inWishList\">\n </rolatech-property-info>\n <!-- <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer> -->\n @if (features.length > 0) {\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n }\n @if (highlights.length > 0) {\n <rolatech-property-highlights [highlights]=\"highlights\"></rolatech-property-highlights>\n }\n @if (floorplans.length > 0) {\n <rolatech-property-floorplan [floorplans]=\"floorplans\"></rolatech-property-floorplan>\n }\n @if (property.location) {\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n }\n @if (videos() && videos().length > 0) {\n @for (item of videos(); track $index) {\n <rolatech-property-video [video]=\"item\"></rolatech-property-video>\n }\n }\n @if (epc && epc.currentScore) {\n <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc>\n }\n <div class=\"flex flex-col\">\n <div class=\"text-2xl font-bold pt-3\" i18n>Sections</div>\n @for (section of property.sections; track $index) {\n <rolatech-property-section [section]=\"section\"></rolatech-property-section>\n }\n <!-- <rolatech-comments [itemId]=\"property.id\"></rolatech-comments> -->\n </div>\n </div>\n <div class=\"w-full md:w-1/3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-property-actions\n [property]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-property-action-contact [email]=\"user.email\" [phone]=\"user.phone\"></rolatech-property-action-contact>\n }\n </rolatech-property-actions>\n </div>\n </div>\n }\n }\n </div>\n</rolatech-container>\n" }]
|
|
4088
4147
|
}], ctorParameters: () => [] });
|
|
4089
4148
|
|
|
4090
|
-
const MY_FORMATS$
|
|
4149
|
+
const MY_FORMATS$4 = {
|
|
4091
4150
|
parse: {
|
|
4092
4151
|
dateInput: 'YYYY-MM-DD',
|
|
4093
4152
|
},
|
|
@@ -4159,7 +4218,7 @@ class PropertyViewingTimeComponent {
|
|
|
4159
4218
|
useClass: MomentDateAdapter,
|
|
4160
4219
|
deps: [MAT_DATE_LOCALE],
|
|
4161
4220
|
},
|
|
4162
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
4221
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
|
|
4163
4222
|
], ngImport: i0, template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [ngModel]=\"proposedTime().date || null\"\n (dateInput)=\"onDateInput($event)\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [ngModel]=\"proposedTime().time || null\" (ngModelChange)=\"onTimeChange($event)\" required>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }] });
|
|
4164
4223
|
}
|
|
4165
4224
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingTimeComponent, decorators: [{
|
|
@@ -4179,7 +4238,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4179
4238
|
useClass: MomentDateAdapter,
|
|
4180
4239
|
deps: [MAT_DATE_LOCALE],
|
|
4181
4240
|
},
|
|
4182
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
4241
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$4 },
|
|
4183
4242
|
], template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [ngModel]=\"proposedTime().date || null\"\n (dateInput)=\"onDateInput($event)\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [ngModel]=\"proposedTime().time || null\" (ngModelChange)=\"onTimeChange($event)\" required>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
|
|
4184
4243
|
}], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
|
|
4185
4244
|
|
|
@@ -4304,7 +4363,7 @@ class PropertyViewingRequestComponent extends BaseComponent {
|
|
|
4304
4363
|
this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
|
|
4305
4364
|
}
|
|
4306
4365
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4307
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", usesInheritance: true, ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? 'Buyer workflow' : 'Property enquiry' }}\n </span>\n <div class=\"property-viewing-request__title\" i18n>Request a viewing</div>\n <p class=\"property-viewing-request__description\" i18n>\n Choose the best slots for your visit and we will confirm the final time with the listing team.\n </p>\n </div>\n\n <div intent-body>\n <div class=\"property-viewing-request__form\">\n @if (!isAgentFlow) {\n <div class=\"mb-3\">\n <mat-radio-group aria-label=\"Select an option\" [(ngModel)]=\"viewing.viewerCategory\">\n <mat-radio-button [value]=\"viewerCategory.TENANT\" i18n>I'm a tenant</mat-radio-button>\n <mat-radio-button [value]=\"viewerCategory.AGENT\" i18n>I'm an agent</mat-radio-button>\n </mat-radio-group>\n </div>\n }\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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" }] });
|
|
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" }] });
|
|
4308
4367
|
}
|
|
4309
4368
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingRequestComponent, decorators: [{
|
|
4310
4369
|
type: Component,
|
|
@@ -4322,7 +4381,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4322
4381
|
PropertyViewingTimeComponent,
|
|
4323
4382
|
PropertyIntentShell,
|
|
4324
4383
|
PropertyMarketBreadcrumbComponent,
|
|
4325
|
-
], template: "<rolatech-property-intent-shell [property]=\"property\">\n <div intent-header class=\"property-viewing-request__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-viewing-request__breadcrumb\"\n [items]=\"breadcrumbItems()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-viewing-request__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow ? '
|
|
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"] }]
|
|
4326
4385
|
}], ctorParameters: () => [] });
|
|
4327
4386
|
|
|
4328
4387
|
class PropertyOfferIndexComponent extends BaseComponent {
|
|
@@ -4571,6 +4630,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4571
4630
|
|
|
4572
4631
|
class OfferHeaderCard {
|
|
4573
4632
|
ctx = inject(OfferDetailContext);
|
|
4633
|
+
offerStatusLabel = offerStatusLabel;
|
|
4574
4634
|
ok = new Set([
|
|
4575
4635
|
PropertyOfferStatus$1.ACCEPTED,
|
|
4576
4636
|
PropertyOfferStatus$1.REFERENCES_PASSED,
|
|
@@ -4605,15 +4665,15 @@ class OfferHeaderCard {
|
|
|
4605
4665
|
]);
|
|
4606
4666
|
statusBadgeClass(status) {
|
|
4607
4667
|
if (!status)
|
|
4608
|
-
return '
|
|
4668
|
+
return 'offer-header-card__status offer-header-card__status--neutral';
|
|
4609
4669
|
if (this.ok.has(status))
|
|
4610
|
-
return '
|
|
4670
|
+
return 'offer-header-card__status offer-header-card__status--positive';
|
|
4611
4671
|
if (this.warn.has(status))
|
|
4612
|
-
return '
|
|
4672
|
+
return 'offer-header-card__status offer-header-card__status--warning';
|
|
4613
4673
|
if (this.bad.has(status))
|
|
4614
|
-
return '
|
|
4674
|
+
return 'offer-header-card__status offer-header-card__status--negative';
|
|
4615
4675
|
// DRAFT or unknown
|
|
4616
|
-
return '
|
|
4676
|
+
return 'offer-header-card__status offer-header-card__status--neutral';
|
|
4617
4677
|
}
|
|
4618
4678
|
statusHint(status) {
|
|
4619
4679
|
if (!status)
|
|
@@ -4673,270 +4733,215 @@ class OfferHeaderCard {
|
|
|
4673
4733
|
return '';
|
|
4674
4734
|
}
|
|
4675
4735
|
}
|
|
4736
|
+
typeLabel(value) {
|
|
4737
|
+
if (!value) {
|
|
4738
|
+
return 'Offer';
|
|
4739
|
+
}
|
|
4740
|
+
return `${String(value).toLowerCase().replace(/^\w/, (char) => char.toUpperCase())} offer`;
|
|
4741
|
+
}
|
|
4676
4742
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4677
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHeaderCard, isStandalone: true, selector: "rolatech-offer-header-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4743
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferHeaderCard, isStandalone: true, selector: "rolatech-offer-header-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-header-card\">\n <div class=\"offer-header-card__body\">\n <div class=\"offer-header-card__copy\">\n <span class=\"offer-header-card__eyebrow\">{{ typeLabel(o.type) }}</span>\n\n <div class=\"offer-header-card__heading\">\n <div class=\"offer-header-card__heading-copy\">\n <h2 class=\"offer-header-card__title\">Offer #{{ o.id }}</h2>\n <p class=\"offer-header-card__description\">{{ statusHint(o.status) }}</p>\n </div>\n\n <span [ngClass]=\"statusBadgeClass(o.status)\">{{ offerStatusLabel(o.status) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__facts\">\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Offer type</span>\n <strong class=\"offer-header-card__fact-value\">{{ typeLabel(o.type) }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Invoice style</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">{{ o.type === 'RENTAL' ? 'Applicants' : 'Payment method' }}</span>\n <strong class=\"offer-header-card__fact-value\">\n @if (o.type === 'RENTAL') {\n {{ o.tenants?.length ?? 0 }}\n } @else {\n {{ ctx.asText(o.sale?.paymentMethod) }}\n }\n </strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Property reference</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.asText(o.propertyId) }}</strong>\n </article>\n </div>\n\n <div class=\"offer-header-card__meta\">\n <span class=\"offer-header-card__meta-chip\">Applicant ID {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Agent ID {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Property ID {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__actions\">\n <ng-content></ng-content>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-header-card{display:grid;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 44%),linear-gradient(140deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%)),var(--rt-base-background, #ffffff);box-shadow:0 22px 52px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.offer-header-card__body{display:flex;flex-direction:column;gap:1rem}.offer-header-card__copy,.offer-header-card__heading-copy{display:flex;flex-direction:column;gap:.55rem}.offer-header-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.38rem .72rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-header-card__heading{display:flex;flex-direction:column;gap:.85rem}.offer-header-card__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.3rem,2vw,1.75rem);line-height:1.1;font-weight:800}.offer-header-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-header-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.4rem;padding:.45rem .85rem;border:1px solid transparent;border-radius:999px;font-size:.85rem;font-weight:700}.offer-header-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-header-card__status--warning{border-color:#f59e0b47;background:#f59e0b1f;color:#a16207}.offer-header-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-header-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);color:var(--rt-text-secondary)}.offer-header-card__facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-header-card__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-header-card__fact-label{color:var(--rt-text-secondary);font-size:.78rem}.offer-header-card__fact-value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.35}.offer-header-card__meta{display:flex;flex-wrap:wrap;gap:.55rem}.offer-header-card__meta-chip{display:inline-flex;align-items:center;min-height:2rem;padding:.4rem .72rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-header-card__actions{display:flex;align-items:flex-start;justify-content:flex-end}@media(min-width:960px){.offer-header-card{grid-template-columns:minmax(0,1.7fr) auto;align-items:start}.offer-header-card__heading{flex-direction:row;align-items:flex-start;justify-content:space-between}.offer-header-card__facts{grid-template-columns:repeat(4,minmax(0,1fr))}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
|
|
4678
4744
|
}
|
|
4679
4745
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferHeaderCard, decorators: [{
|
|
4680
4746
|
type: Component,
|
|
4681
|
-
args: [{ selector: 'rolatech-offer-header-card', imports: [NgClass], template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4747
|
+
args: [{ selector: 'rolatech-offer-header-card', imports: [NgClass], template: "@if (ctx.offer(); as o) {\n<section class=\"offer-header-card\">\n <div class=\"offer-header-card__body\">\n <div class=\"offer-header-card__copy\">\n <span class=\"offer-header-card__eyebrow\">{{ typeLabel(o.type) }}</span>\n\n <div class=\"offer-header-card__heading\">\n <div class=\"offer-header-card__heading-copy\">\n <h2 class=\"offer-header-card__title\">Offer #{{ o.id }}</h2>\n <p class=\"offer-header-card__description\">{{ statusHint(o.status) }}</p>\n </div>\n\n <span [ngClass]=\"statusBadgeClass(o.status)\">{{ offerStatusLabel(o.status) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__facts\">\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Offer type</span>\n <strong class=\"offer-header-card__fact-value\">{{ typeLabel(o.type) }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Invoice style</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">{{ o.type === 'RENTAL' ? 'Applicants' : 'Payment method' }}</span>\n <strong class=\"offer-header-card__fact-value\">\n @if (o.type === 'RENTAL') {\n {{ o.tenants?.length ?? 0 }}\n } @else {\n {{ ctx.asText(o.sale?.paymentMethod) }}\n }\n </strong>\n </article>\n <article class=\"offer-header-card__fact\">\n <span class=\"offer-header-card__fact-label\">Property reference</span>\n <strong class=\"offer-header-card__fact-value\">{{ ctx.asText(o.propertyId) }}</strong>\n </article>\n </div>\n\n <div class=\"offer-header-card__meta\">\n <span class=\"offer-header-card__meta-chip\">Applicant ID {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Agent ID {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-header-card__meta-chip\">Property ID {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n\n <div class=\"offer-header-card__actions\">\n <ng-content></ng-content>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-header-card{display:grid;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 44%),linear-gradient(140deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 92%,var(--rt-brand-color) 8%)),var(--rt-base-background, #ffffff);box-shadow:0 22px 52px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.offer-header-card__body{display:flex;flex-direction:column;gap:1rem}.offer-header-card__copy,.offer-header-card__heading-copy{display:flex;flex-direction:column;gap:.55rem}.offer-header-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.38rem .72rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-header-card__heading{display:flex;flex-direction:column;gap:.85rem}.offer-header-card__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.3rem,2vw,1.75rem);line-height:1.1;font-weight:800}.offer-header-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-header-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2.4rem;padding:.45rem .85rem;border:1px solid transparent;border-radius:999px;font-size:.85rem;font-weight:700}.offer-header-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-header-card__status--warning{border-color:#f59e0b47;background:#f59e0b1f;color:#a16207}.offer-header-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-header-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);color:var(--rt-text-secondary)}.offer-header-card__facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.offer-header-card__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.05rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 86%,var(--rt-brand-color) 14%)}.offer-header-card__fact-label{color:var(--rt-text-secondary);font-size:.78rem}.offer-header-card__fact-value{color:var(--rt-text-primary);font-size:.95rem;line-height:1.35}.offer-header-card__meta{display:flex;flex-wrap:wrap;gap:.55rem}.offer-header-card__meta-chip{display:inline-flex;align-items:center;min-height:2rem;padding:.4rem .72rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%);color:var(--rt-text-secondary);font-size:.8rem}.offer-header-card__actions{display:flex;align-items:flex-start;justify-content:flex-end}@media(min-width:960px){.offer-header-card{grid-template-columns:minmax(0,1.7fr) auto;align-items:start}.offer-header-card__heading{flex-direction:row;align-items:flex-start;justify-content:space-between}.offer-header-card__facts{grid-template-columns:repeat(4,minmax(0,1fr))}}\n"] }]
|
|
4682
4748
|
}] });
|
|
4683
4749
|
|
|
4684
4750
|
class OfferSideSummaryCard {
|
|
4685
4751
|
ctx = inject(OfferDetailContext);
|
|
4752
|
+
statusLabel = offerStatusLabel;
|
|
4686
4753
|
salePaymentMethodLabel = {
|
|
4687
4754
|
CASH: 'Cash',
|
|
4688
4755
|
MORTGAGE: 'Mortgage',
|
|
4689
4756
|
};
|
|
4757
|
+
typeLabel(value) {
|
|
4758
|
+
if (!value) {
|
|
4759
|
+
return 'Offer';
|
|
4760
|
+
}
|
|
4761
|
+
return `${String(value).toLowerCase().replace(/^\w/, (char) => char.toUpperCase())} offer`;
|
|
4762
|
+
}
|
|
4763
|
+
statusToneClass(status) {
|
|
4764
|
+
switch (status) {
|
|
4765
|
+
case PropertyOfferStatus$1.ACCEPTED:
|
|
4766
|
+
case PropertyOfferStatus$1.REFERENCES_PASSED:
|
|
4767
|
+
case PropertyOfferStatus$1.CONTRACT_SIGNED:
|
|
4768
|
+
case PropertyOfferStatus$1.MOVE_IN_PAYMENT_PAID:
|
|
4769
|
+
case PropertyOfferStatus$1.COMPLETED:
|
|
4770
|
+
return 'offer-side-summary-card__status offer-side-summary-card__status--positive';
|
|
4771
|
+
case PropertyOfferStatus$1.REJECTED:
|
|
4772
|
+
case PropertyOfferStatus$1.REFERENCES_FAILED:
|
|
4773
|
+
case PropertyOfferStatus$1.CONTRACT_FAILED:
|
|
4774
|
+
case PropertyOfferStatus$1.CANCELLED:
|
|
4775
|
+
case PropertyOfferStatus$1.FAILED:
|
|
4776
|
+
case PropertyOfferStatus$1.EXPIRED:
|
|
4777
|
+
return 'offer-side-summary-card__status offer-side-summary-card__status--negative';
|
|
4778
|
+
default:
|
|
4779
|
+
return 'offer-side-summary-card__status offer-side-summary-card__status--neutral';
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
leadAmount(offer) {
|
|
4783
|
+
if (offer.type === 'RENTAL') {
|
|
4784
|
+
return offer.rentalTerms?.amount ?? offer.item.amount;
|
|
4785
|
+
}
|
|
4786
|
+
return offer.sale?.amount ?? offer.item.amount;
|
|
4787
|
+
}
|
|
4788
|
+
nextStepCopy(offer) {
|
|
4789
|
+
switch (offer.status) {
|
|
4790
|
+
case PropertyOfferStatus$1.SUBMITTED:
|
|
4791
|
+
case PropertyOfferStatus$1.VIEWED_BY_AGENT:
|
|
4792
|
+
case PropertyOfferStatus$1.VIEWED_BY_TENANT:
|
|
4793
|
+
return 'The latest offer is waiting for a response.';
|
|
4794
|
+
case PropertyOfferStatus$1.COUNTERED_BY_AGENT:
|
|
4795
|
+
case PropertyOfferStatus$1.COUNTERED_BY_TENANT:
|
|
4796
|
+
return 'Compare the latest version against the previous one before responding.';
|
|
4797
|
+
case PropertyOfferStatus$1.ACCEPTED:
|
|
4798
|
+
case PropertyOfferStatus$1.UNDER_OFFER:
|
|
4799
|
+
return 'The negotiation has moved forward into the next workflow step.';
|
|
4800
|
+
case PropertyOfferStatus$1.REFERENCING:
|
|
4801
|
+
return 'Referencing checks are in progress.';
|
|
4802
|
+
case PropertyOfferStatus$1.COMPLETED:
|
|
4803
|
+
return 'The offer flow is complete and kept here as a record.';
|
|
4804
|
+
default:
|
|
4805
|
+
return 'Use this summary to keep the main terms and references visible while reviewing the detail page.';
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4690
4808
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4691
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4809
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-side-summary-card\">\n <div class=\"offer-side-summary-card__hero\">\n <span class=\"offer-side-summary-card__eyebrow\">Quick summary</span>\n <div class=\"offer-side-summary-card__amount\">{{ leadAmount(o) | price }}</div>\n <p class=\"offer-side-summary-card__description\">{{ nextStepCopy(o) }}</p>\n <span [class]=\"statusToneClass(o.status)\">{{ statusLabel(o.status) }}</span>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Offer details</h3>\n <div class=\"offer-side-summary-card__rows\">\n <div class=\"offer-side-summary-card__row\">\n <span>Type</span>\n <strong>{{ typeLabel(o.type) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Invoice</span>\n <strong>{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Listing amount</span>\n <strong>{{ o.item.amount | price }}</strong>\n </div>\n @if (ctx.isRental()) {\n <div class=\"offer-side-summary-card__row\">\n <span>Applicants</span>\n <strong>{{ o.tenants?.length ?? 0 }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Move-in</span>\n <strong>{{ o.rentalTerms?.moveInDate ? (o.rentalTerms?.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </div>\n } @else {\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer method</span>\n <strong>{{ salePaymentMethodLabel[o.sale?.paymentMethod!] || ctx.asText(o.sale?.paymentMethod) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer</span>\n <strong>{{ o.sale?.buyerName ?? '\u2014' }}</strong>\n </div>\n }\n </div>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Route references</h3>\n <div class=\"offer-side-summary-card__chips\">\n <span class=\"offer-side-summary-card__chip\">User {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Agent {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Property {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-side-summary-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.05rem;background:linear-gradient(150deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%))}.offer-side-summary-card__hero,.offer-side-summary-card__section{display:flex;flex-direction:column;gap:.6rem}.offer-side-summary-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-side-summary-card__amount{color:var(--rt-text-primary);font-size:clamp(1.55rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-side-summary-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-side-summary-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2rem;padding:.35rem .7rem;border:1px solid transparent;border-radius:999px;font-size:.8rem;font-weight:700}.offer-side-summary-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-side-summary-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-side-summary-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-base-background, #ffffff);color:var(--rt-text-primary)}.offer-side-summary-card__section{padding-top:.9rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-side-summary-card__section-title{margin:0;color:var(--rt-text-primary);font-size:.96rem;font-weight:720}.offer-side-summary-card__rows{display:flex;flex-direction:column;gap:.7rem}.offer-side-summary-card__row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;color:var(--rt-text-secondary);font-size:.88rem}.offer-side-summary-card__row strong{color:var(--rt-text-primary);text-align:right}.offer-side-summary-card__chips{display:flex;flex-wrap:wrap;gap:.5rem}.offer-side-summary-card__chip{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
4692
4810
|
}
|
|
4693
4811
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: OfferSideSummaryCard, decorators: [{
|
|
4694
4812
|
type: Component,
|
|
4695
|
-
args: [{ selector: 'rolatech-offer-side-summary-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"
|
|
4813
|
+
args: [{ selector: 'rolatech-offer-side-summary-card', imports: [CommonModule, PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"offer-side-summary-card\">\n <div class=\"offer-side-summary-card__hero\">\n <span class=\"offer-side-summary-card__eyebrow\">Quick summary</span>\n <div class=\"offer-side-summary-card__amount\">{{ leadAmount(o) | price }}</div>\n <p class=\"offer-side-summary-card__description\">{{ nextStepCopy(o) }}</p>\n <span [class]=\"statusToneClass(o.status)\">{{ statusLabel(o.status) }}</span>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Offer details</h3>\n <div class=\"offer-side-summary-card__rows\">\n <div class=\"offer-side-summary-card__row\">\n <span>Type</span>\n <strong>{{ typeLabel(o.type) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Invoice</span>\n <strong>{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Listing amount</span>\n <strong>{{ o.item.amount | price }}</strong>\n </div>\n @if (ctx.isRental()) {\n <div class=\"offer-side-summary-card__row\">\n <span>Applicants</span>\n <strong>{{ o.tenants?.length ?? 0 }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Move-in</span>\n <strong>{{ o.rentalTerms?.moveInDate ? (o.rentalTerms?.moveInDate | date: 'mediumDate') : '\u2014' }}</strong>\n </div>\n } @else {\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer method</span>\n <strong>{{ salePaymentMethodLabel[o.sale?.paymentMethod!] || ctx.asText(o.sale?.paymentMethod) }}</strong>\n </div>\n <div class=\"offer-side-summary-card__row\">\n <span>Buyer</span>\n <strong>{{ o.sale?.buyerName ?? '\u2014' }}</strong>\n </div>\n }\n </div>\n </div>\n\n <div class=\"offer-side-summary-card__section\">\n <h3 class=\"offer-side-summary-card__section-title\">Route references</h3>\n <div class=\"offer-side-summary-card__chips\">\n <span class=\"offer-side-summary-card__chip\">User {{ ctx.asText(o.userId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Agent {{ ctx.asText(o.agentId) }}</span>\n <span class=\"offer-side-summary-card__chip\">Property {{ ctx.asText(o.propertyId) }}</span>\n </div>\n </div>\n</section>\n}\n", styles: [":host{display:block}.offer-side-summary-card{display:flex;flex-direction:column;gap:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.4rem;padding:1.05rem;background:linear-gradient(150deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%))}.offer-side-summary-card__hero,.offer-side-summary-card__section{display:flex;flex-direction:column;gap:.6rem}.offer-side-summary-card__eyebrow{display:inline-flex;align-self:flex-start;padding:.28rem .62rem;border-radius:999px;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.offer-side-summary-card__amount{color:var(--rt-text-primary);font-size:clamp(1.55rem,2.4vw,2rem);line-height:1;font-weight:800}.offer-side-summary-card__description{margin:0;color:var(--rt-text-secondary);line-height:1.65}.offer-side-summary-card__status{display:inline-flex;align-self:flex-start;align-items:center;min-height:2rem;padding:.35rem .7rem;border:1px solid transparent;border-radius:999px;font-size:.8rem;font-weight:700}.offer-side-summary-card__status--positive{border-color:#22c55e40;background:#22c55e1f;color:#15803d}.offer-side-summary-card__status--negative{border-color:#ef44443d;background:#ef44441a;color:#b91c1c}.offer-side-summary-card__status--neutral{border-color:var(--rt-border-color, rgba(15, 23, 42, .08));background:var(--rt-base-background, #ffffff);color:var(--rt-text-primary)}.offer-side-summary-card__section{padding-top:.9rem;border-top:1px solid var(--rt-border-color, rgba(15, 23, 42, .08))}.offer-side-summary-card__section-title{margin:0;color:var(--rt-text-primary);font-size:.96rem;font-weight:720}.offer-side-summary-card__rows{display:flex;flex-direction:column;gap:.7rem}.offer-side-summary-card__row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;color:var(--rt-text-secondary);font-size:.88rem}.offer-side-summary-card__row strong{color:var(--rt-text-primary);text-align:right}.offer-side-summary-card__chips{display:flex;flex-wrap:wrap;gap:.5rem}.offer-side-summary-card__chip{display:inline-flex;align-items:center;min-height:1.9rem;padding:.3rem .62rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:999px;background:var(--rt-base-background, #ffffff);color:var(--rt-text-secondary);font-size:.76rem}\n"] }]
|
|
4696
4814
|
}] });
|
|
4697
4815
|
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
}
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
};
|
|
4816
|
+
class PropertyWorkspaceShell {
|
|
4817
|
+
eyebrow = input('Workspace', ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
|
|
4818
|
+
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
4819
|
+
description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
|
|
4820
|
+
stats = input([], ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
4821
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4822
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyWorkspaceShell, isStandalone: true, selector: "rolatech-property-workspace-shell", inputs: { eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, stats: { classPropertyName: "stats", publicName: "stats", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
4823
|
+
}
|
|
4824
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, decorators: [{
|
|
4825
|
+
type: Component,
|
|
4826
|
+
args: [{ selector: 'rolatech-property-workspace-shell', imports: [CommonModule], template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"] }]
|
|
4827
|
+
}], propDecorators: { eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], stats: [{ type: i0.Input, args: [{ isSignal: true, alias: "stats", required: false }] }] } });
|
|
4828
|
+
|
|
4709
4829
|
class PropertyOfferDetailComponent extends BaseComponent {
|
|
4710
|
-
propertyService = inject(PropertyService);
|
|
4711
4830
|
propertyOfferService = inject(PropertyOfferService);
|
|
4712
|
-
propertyOfferCounterService = inject(PropertyOfferCounterService);
|
|
4713
4831
|
f = inject(PropertyOfferNegotiationFacade);
|
|
4714
|
-
authUserService = inject(AuthUserService);
|
|
4715
|
-
paymentService = inject(PaymentService);
|
|
4716
4832
|
ctx = inject(OfferDetailContext);
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
});
|
|
4748
|
-
this.route.queryParams.subscribe(async (params) => {
|
|
4749
|
-
const sessionId = params['session_id'];
|
|
4750
|
-
if (sessionId) {
|
|
4751
|
-
this.propertyOfferService.checkOfferPaymentStatus(this.id, sessionId).subscribe({
|
|
4752
|
-
next: (res) => {
|
|
4753
|
-
if (res.status === 'paid') {
|
|
4754
|
-
this.offer.status = PropertyOfferStatus.HOLDING_DEPOSIT_PAID;
|
|
4755
|
-
// Show success UI
|
|
4756
|
-
}
|
|
4757
|
-
},
|
|
4758
|
-
});
|
|
4759
|
-
}
|
|
4760
|
-
});
|
|
4761
|
-
}
|
|
4762
|
-
ngOnDestroy() {
|
|
4763
|
-
this.checkouting = false;
|
|
4764
|
-
}
|
|
4765
|
-
get(id) {
|
|
4766
|
-
this.propertyOfferService.getOffer(id).subscribe({
|
|
4767
|
-
next: (res) => {
|
|
4768
|
-
this.offer = res.data;
|
|
4769
|
-
this.getProperty(this.offer.propertyId);
|
|
4770
|
-
this.calculateDeposit();
|
|
4771
|
-
// this.findAgentInfo();
|
|
4772
|
-
},
|
|
4773
|
-
});
|
|
4774
|
-
}
|
|
4775
|
-
getViewByTenant(id) {
|
|
4776
|
-
this.propertyOfferCounterService.getNegotiationViewByTenant(id).subscribe({
|
|
4777
|
-
next: (res) => {
|
|
4778
|
-
this.view = res.data;
|
|
4779
|
-
},
|
|
4780
|
-
});
|
|
4781
|
-
}
|
|
4782
|
-
timeline() {
|
|
4783
|
-
this.info = true;
|
|
4784
|
-
this.loadingTimeline = true;
|
|
4785
|
-
this.propertyOfferService.offerTimeline(this.id).subscribe({
|
|
4786
|
-
next: (res) => {
|
|
4787
|
-
this.timelineData = res.data;
|
|
4788
|
-
this.loadingTimeline = false;
|
|
4789
|
-
},
|
|
4790
|
-
error: (error) => {
|
|
4791
|
-
this.loadingTimeline = false;
|
|
4792
|
-
},
|
|
4793
|
-
});
|
|
4794
|
-
}
|
|
4795
|
-
getProperty(propertyId) {
|
|
4796
|
-
this.propertyService.get(propertyId).subscribe({
|
|
4797
|
-
next: (res) => {
|
|
4798
|
-
this.property = res.data;
|
|
4799
|
-
this.getAgentPublicInfo(this.property.agentId);
|
|
4800
|
-
},
|
|
4801
|
-
});
|
|
4802
|
-
}
|
|
4803
|
-
getAgentPublicInfo(agentId) {
|
|
4804
|
-
this.authUserService.getPublicUserInfo(agentId).subscribe({
|
|
4805
|
-
next: (res) => {
|
|
4806
|
-
this.agent = res;
|
|
4807
|
-
},
|
|
4808
|
-
});
|
|
4809
|
-
}
|
|
4810
|
-
cancel() {
|
|
4811
|
-
this.propertyOfferService.cancelOffer(this.id).subscribe({
|
|
4812
|
-
next: (res) => {
|
|
4813
|
-
this.offer.status = 'CANCELLED';
|
|
4814
|
-
this.snackBarService.open('Cancelled');
|
|
4815
|
-
},
|
|
4816
|
-
error: (error) => {
|
|
4817
|
-
this.snackBarService.open(error.message);
|
|
4818
|
-
},
|
|
4819
|
-
});
|
|
4820
|
-
}
|
|
4821
|
-
calculateDeposit() {
|
|
4822
|
-
const price = this.offer.item.amount;
|
|
4823
|
-
this.holdingDeposit = ((price * 12) / 52).toFixed(2);
|
|
4824
|
-
this.securityDeposit = (((price * 12) / 52) * 5).toFixed(2);
|
|
4825
|
-
}
|
|
4826
|
-
holdingDepositCheckout() {
|
|
4827
|
-
this.checkouting = true;
|
|
4828
|
-
const data = {
|
|
4829
|
-
businessType: 'OFFER',
|
|
4830
|
-
businessId: this.id,
|
|
4831
|
-
provider: 'STRIPE',
|
|
4832
|
-
method: 'STRIPE_CHECKOUT',
|
|
4833
|
-
purpose: 'HOLDING_DEPOSIT',
|
|
4834
|
-
};
|
|
4835
|
-
this.paymentService
|
|
4836
|
-
.createPaymentIntent(data)
|
|
4837
|
-
.pipe(switchMap((res) => {
|
|
4838
|
-
const intent = {
|
|
4839
|
-
intentId: res.data.id,
|
|
4840
|
-
provider: 'STRIPE',
|
|
4841
|
-
method: 'STRIPE_CHECKOUT',
|
|
4842
|
-
successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
|
|
4843
|
-
cancelUrl: window.location.href,
|
|
4844
|
-
};
|
|
4845
|
-
return this.paymentService.createPayment(intent);
|
|
4846
|
-
}))
|
|
4847
|
-
.subscribe({
|
|
4848
|
-
next: (res) => {
|
|
4849
|
-
this.checkouting = false;
|
|
4850
|
-
const { checkoutUrl } = res.data;
|
|
4851
|
-
window.open(checkoutUrl, '_blank');
|
|
4852
|
-
},
|
|
4853
|
-
error: (error) => {
|
|
4854
|
-
this.checkouting = false;
|
|
4855
|
-
},
|
|
4856
|
-
});
|
|
4857
|
-
}
|
|
4858
|
-
securityDepositCheckout() {
|
|
4859
|
-
this.checkouting = true;
|
|
4860
|
-
const data = {
|
|
4861
|
-
successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
|
|
4862
|
-
cancelUrl: window.location.href,
|
|
4863
|
-
};
|
|
4864
|
-
this.propertyOfferService.createSecurityDepositCheckout(this.id, data).subscribe({
|
|
4865
|
-
next: (res) => {
|
|
4866
|
-
window.location.href = res.data.checkoutUrl;
|
|
4867
|
-
},
|
|
4868
|
-
error: (error) => {
|
|
4869
|
-
this.checkouting = false;
|
|
4833
|
+
mode = input('tenant', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
4834
|
+
market = input('tenant', ...(ngDevMode ? [{ debugName: "market" }] : []));
|
|
4835
|
+
eyebrow = input(null, ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
|
|
4836
|
+
title = input(null, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
4837
|
+
description = input(null, ...(ngDevMode ? [{ debugName: "description" }] : []));
|
|
4838
|
+
backLabel = input(null, ...(ngDevMode ? [{ debugName: "backLabel" }] : []));
|
|
4839
|
+
currentOfferId = this.id ?? null;
|
|
4840
|
+
statusLabel = computed(() => offerStatusLabel(this.ctx.offer()?.status), ...(ngDevMode ? [{ debugName: "statusLabel" }] : []));
|
|
4841
|
+
resolvedEyebrow = computed(() => this.eyebrow() ?? (this.mode() === 'agent' ? 'Market workflow' : 'My applications'), ...(ngDevMode ? [{ debugName: "resolvedEyebrow" }] : []));
|
|
4842
|
+
resolvedTitle = computed(() => this.title() ?? (this.mode() === 'agent' ? 'Offer detail' : 'Offer detail'), ...(ngDevMode ? [{ debugName: "resolvedTitle" }] : []));
|
|
4843
|
+
resolvedDescription = computed(() => this.description() ??
|
|
4844
|
+
(this.mode() === 'agent'
|
|
4845
|
+
? 'Track buyer-side negotiations, compare counter-offers, and keep the deal moving from one shared workspace.'
|
|
4846
|
+
: 'Review every negotiation update, compare offer versions, and keep the next step close at hand.'), ...(ngDevMode ? [{ debugName: "resolvedDescription" }] : []));
|
|
4847
|
+
stats = computed(() => {
|
|
4848
|
+
const offer = this.ctx.offer();
|
|
4849
|
+
if (!offer) {
|
|
4850
|
+
return [
|
|
4851
|
+
{ label: 'Status', value: 'Loading', hint: 'Checking the current negotiation state' },
|
|
4852
|
+
{ label: 'Type', value: 'Loading', hint: 'Resolving the submitted offer type' },
|
|
4853
|
+
{ label: 'Applicants', value: 'Loading', hint: 'Looking up the latest submitted party count' },
|
|
4854
|
+
{ label: 'Offer amount', value: 'Loading', hint: 'Fetching the latest property snapshot' },
|
|
4855
|
+
];
|
|
4856
|
+
}
|
|
4857
|
+
const isRental = offer.type === 'RENTAL';
|
|
4858
|
+
return [
|
|
4859
|
+
{
|
|
4860
|
+
label: 'Status',
|
|
4861
|
+
value: this.statusLabel(),
|
|
4862
|
+
hint: this.mode() === 'agent' ? 'Latest buyer-side negotiation state' : 'Latest response from the listing side',
|
|
4870
4863
|
},
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4864
|
+
{
|
|
4865
|
+
label: 'Type',
|
|
4866
|
+
value: this.humanize(offer.type),
|
|
4867
|
+
hint: isRental ? 'Rental negotiation workflow' : 'Sale negotiation workflow',
|
|
4868
|
+
},
|
|
4869
|
+
isRental
|
|
4870
|
+
? {
|
|
4871
|
+
label: 'Applicants',
|
|
4872
|
+
value: offer.tenants?.length ?? 0,
|
|
4873
|
+
hint: 'People included in this submission',
|
|
4874
|
+
}
|
|
4875
|
+
: {
|
|
4876
|
+
label: 'Funding',
|
|
4877
|
+
value: this.humanize(offer.sale?.paymentMethod),
|
|
4878
|
+
hint: 'Declared buyer payment method',
|
|
4879
|
+
},
|
|
4880
|
+
{
|
|
4881
|
+
label: 'Offer amount',
|
|
4882
|
+
value: this.formatMoney(offer.item?.amount),
|
|
4883
|
+
hint: offer.item?.title || 'Property snapshot attached to this offer',
|
|
4878
4884
|
},
|
|
4879
|
-
|
|
4880
|
-
}
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
this.f.reject(notes);
|
|
4885
|
+
];
|
|
4886
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
4887
|
+
ngOnInit() {
|
|
4888
|
+
this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
|
4889
|
+
const id = params.get('id');
|
|
4890
|
+
this.currentOfferId = id;
|
|
4891
|
+
if (!id) {
|
|
4892
|
+
return;
|
|
4888
4893
|
}
|
|
4894
|
+
this.ctx.init(id);
|
|
4895
|
+
void this.f.load(id, this.market());
|
|
4889
4896
|
});
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
ref.afterClosed().subscribe((notes) => {
|
|
4896
|
-
if (notes) {
|
|
4897
|
-
this.f.markReferencesFailed(notes);
|
|
4897
|
+
this.route.queryParamMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
|
4898
|
+
const sessionId = params.get('session_id');
|
|
4899
|
+
const offerId = this.currentOfferId;
|
|
4900
|
+
if (!offerId || !sessionId) {
|
|
4901
|
+
return;
|
|
4898
4902
|
}
|
|
4903
|
+
this.propertyOfferService.checkOfferPaymentStatus(offerId, sessionId).subscribe({
|
|
4904
|
+
next: (res) => {
|
|
4905
|
+
if (res.status === 'paid') {
|
|
4906
|
+
void this.ctx.reloadOffer(offerId);
|
|
4907
|
+
void this.ctx.reloadHistory(offerId);
|
|
4908
|
+
void this.f.load(offerId, this.market());
|
|
4909
|
+
}
|
|
4910
|
+
},
|
|
4911
|
+
});
|
|
4899
4912
|
});
|
|
4900
4913
|
}
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
this.snackBarService.open('Counter sent');
|
|
4921
|
-
},
|
|
4922
|
-
error: (error) => {
|
|
4923
|
-
this.snackBarService.open(error.message);
|
|
4924
|
-
},
|
|
4925
|
-
});
|
|
4926
|
-
}
|
|
4927
|
-
});
|
|
4914
|
+
formatMoney(amount) {
|
|
4915
|
+
if (amount === null || amount === undefined) {
|
|
4916
|
+
return '—';
|
|
4917
|
+
}
|
|
4918
|
+
return new Intl.NumberFormat('en-GB', {
|
|
4919
|
+
style: 'currency',
|
|
4920
|
+
currency: 'GBP',
|
|
4921
|
+
maximumFractionDigits: 0,
|
|
4922
|
+
}).format(amount);
|
|
4923
|
+
}
|
|
4924
|
+
humanize(value) {
|
|
4925
|
+
if (!value) {
|
|
4926
|
+
return '—';
|
|
4927
|
+
}
|
|
4928
|
+
return value
|
|
4929
|
+
.trim()
|
|
4930
|
+
.replace(/[_-]+/g, ' ')
|
|
4931
|
+
.toLowerCase()
|
|
4932
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
4928
4933
|
}
|
|
4929
|
-
withdraw() { }
|
|
4930
4934
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
4931
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", providers: [OfferDetailContext, PropertyOfferNegotiationFacade], usesInheritance: true, ngImport: i0, template: "@if (
|
|
4935
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, market: { classPropertyName: "market", publicName: "market", isSignal: true, isRequired: false, transformFunction: null }, eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, backLabel: { classPropertyName: "backLabel", publicName: "backLabel", isSignal: true, isRequired: false, transformFunction: null } }, providers: [OfferDetailContext, PropertyOfferNegotiationFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-offer-detail\">\n @if (mode() === 'agent') {\n <rolatech-property-workspace-shell\n [eyebrow]=\"resolvedEyebrow()\"\n [title]=\"resolvedTitle()\"\n [description]=\"resolvedDescription()\"\n [stats]=\"stats()\"\n >\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-property-workspace-shell>\n } @else {\n <rolatech-page-collection-shell [eyebrow]=\"resolvedEyebrow()\" [title]=\"resolvedTitle()\" [subtitle]=\"resolvedDescription()\">\n <div page-shell-header-meta class=\"property-offer-detail__header-meta\">\n <div class=\"property-offer-detail__page-actions\">\n <a mat-stroked-button routerLink=\"../\">Back to offers</a>\n </div>\n\n <div class=\"property-offer-detail__summary\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-offer-detail__summary-card\">\n <span class=\"property-offer-detail__summary-value\">{{ stat.value }}</span>\n <span class=\"property-offer-detail__summary-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-offer-detail__summary-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-page-collection-shell>\n }\n\n <ng-template #detailContent>\n @if (ctx.error()) {\n <section class=\"property-offer-detail__error\">{{ ctx.error() }}</section>\n }\n\n @if (ctx.offer()) {\n <div class=\"property-offer-detail__grid\">\n <div class=\"property-offer-detail__main\">\n <rolatech-offer-header-card></rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"property-offer-detail__side\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n } @else {\n <section class=\"property-offer-detail__loading\">Loading offer detail...</section>\n }\n </ng-template>\n</div>\n", styles: [":host{display:block}.property-offer-detail__workspace-actions{display:flex;justify-content:flex-end}.property-offer-detail__header-meta{display:flex;flex-direction:column;gap:.85rem}.property-offer-detail__page-actions{display:flex;justify-content:flex-end}.property-offer-detail__summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:.85rem}.property-offer-detail__summary-card{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-offer-detail__summary-label,.property-offer-detail__summary-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-offer-detail__grid{display:grid;gap:1rem}.property-offer-detail__main,.property-offer-detail__side{display:flex;flex-direction:column;gap:1rem}.property-offer-detail__error,.property-offer-detail__loading{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:var(--rt-base-background, #ffffff);padding:1rem 1.1rem;color:var(--rt-text-secondary)}.property-offer-detail__error{color:#b91c1c;background:color-mix(in srgb,#fff1f2 84%,var(--rt-base-background, #ffffff))}@media(min-width:1024px){.property-offer-detail__summary{grid-template-columns:repeat(4,minmax(0,1fr))}.property-offer-detail__grid{grid-template-columns:minmax(0,1.75fr) minmax(20rem,.9fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: PageCollectionShellComponent, selector: "rolatech-page-collection-shell", inputs: ["eyebrow", "title", "subtitle"] }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }, { kind: "component", type: OfferHeaderCard, selector: "rolatech-offer-header-card" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferSideSummaryCard, selector: "rolatech-offer-side-summary-card" }, { kind: "component", type: OfferHistoryAccordion, selector: "rolatech-offer-history-accordion" }, { kind: "component", type: OfferCounterPreviousCard, selector: "rolatech-offer-counter-previous-card" }, { kind: "component", type: OfferCounterLatestCard, selector: "rolatech-offer-counter-latest-card" }, { kind: "component", type: OfferNegotiationSection, selector: "rolatech-offer-negotiation-section" }] });
|
|
4932
4936
|
}
|
|
4933
4937
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferDetailComponent, decorators: [{
|
|
4934
4938
|
type: Component,
|
|
4935
4939
|
args: [{ selector: 'rolatech-property-offer-detail', imports: [
|
|
4936
4940
|
CommonModule,
|
|
4941
|
+
RouterLink,
|
|
4937
4942
|
MatButtonModule,
|
|
4938
|
-
|
|
4939
|
-
|
|
4943
|
+
PageCollectionShellComponent,
|
|
4944
|
+
PropertyWorkspaceShell,
|
|
4940
4945
|
OfferHeaderCard,
|
|
4941
4946
|
OfferItemCard,
|
|
4942
4947
|
OfferRentalTermsCard,
|
|
@@ -4944,48 +4949,195 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
4944
4949
|
OfferTenantsAccordion,
|
|
4945
4950
|
OfferSideSummaryCard,
|
|
4946
4951
|
OfferHistoryAccordion,
|
|
4947
|
-
MatFormFieldModule,
|
|
4948
|
-
MatInputModule,
|
|
4949
|
-
MatSelectModule,
|
|
4950
|
-
MatDatepickerModule,
|
|
4951
|
-
MatNativeDateModule,
|
|
4952
|
-
MatSlideToggleModule,
|
|
4953
|
-
MatChipsModule,
|
|
4954
|
-
FormsModule,
|
|
4955
4952
|
OfferCounterPreviousCard,
|
|
4956
4953
|
OfferCounterLatestCard,
|
|
4957
4954
|
OfferNegotiationSection,
|
|
4958
|
-
], providers: [OfferDetailContext, PropertyOfferNegotiationFacade], template: "@if (
|
|
4959
|
-
}] });
|
|
4955
|
+
], providers: [OfferDetailContext, PropertyOfferNegotiationFacade], template: "<div class=\"property-offer-detail\">\n @if (mode() === 'agent') {\n <rolatech-property-workspace-shell\n [eyebrow]=\"resolvedEyebrow()\"\n [title]=\"resolvedTitle()\"\n [description]=\"resolvedDescription()\"\n [stats]=\"stats()\"\n >\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-property-workspace-shell>\n } @else {\n <rolatech-page-collection-shell [eyebrow]=\"resolvedEyebrow()\" [title]=\"resolvedTitle()\" [subtitle]=\"resolvedDescription()\">\n <div page-shell-header-meta class=\"property-offer-detail__header-meta\">\n <div class=\"property-offer-detail__page-actions\">\n <a mat-stroked-button routerLink=\"../\">Back to offers</a>\n </div>\n\n <div class=\"property-offer-detail__summary\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-offer-detail__summary-card\">\n <span class=\"property-offer-detail__summary-value\">{{ stat.value }}</span>\n <span class=\"property-offer-detail__summary-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-offer-detail__summary-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n </div>\n\n <ng-container [ngTemplateOutlet]=\"detailContent\"></ng-container>\n </rolatech-page-collection-shell>\n }\n\n <ng-template #detailContent>\n @if (ctx.error()) {\n <section class=\"property-offer-detail__error\">{{ ctx.error() }}</section>\n }\n\n @if (ctx.offer()) {\n <div class=\"property-offer-detail__grid\">\n <div class=\"property-offer-detail__main\">\n <rolatech-offer-header-card></rolatech-offer-header-card>\n <rolatech-offer-negotiation-section />\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n @if (!f.latest()) {\n <rolatech-offer-rental-terms-card />\n } @else {\n <rolatech-offer-counter-latest-card />\n <rolatech-offer-counter-previous-card />\n }\n\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n\n <rolatech-offer-history-accordion />\n </div>\n\n <aside class=\"property-offer-detail__side\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n } @else {\n <section class=\"property-offer-detail__loading\">Loading offer detail...</section>\n }\n </ng-template>\n</div>\n", styles: [":host{display:block}.property-offer-detail__workspace-actions{display:flex;justify-content:flex-end}.property-offer-detail__header-meta{display:flex;flex-direction:column;gap:.85rem}.property-offer-detail__page-actions{display:flex;justify-content:flex-end}.property-offer-detail__summary{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:.85rem}.property-offer-detail__summary-card{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-offer-detail__summary-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-offer-detail__summary-label,.property-offer-detail__summary-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-offer-detail__grid{display:grid;gap:1rem}.property-offer-detail__main,.property-offer-detail__side{display:flex;flex-direction:column;gap:1rem}.property-offer-detail__error,.property-offer-detail__loading{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:var(--rt-base-background, #ffffff);padding:1rem 1.1rem;color:var(--rt-text-secondary)}.property-offer-detail__error{color:#b91c1c;background:color-mix(in srgb,#fff1f2 84%,var(--rt-base-background, #ffffff))}@media(min-width:1024px){.property-offer-detail__summary{grid-template-columns:repeat(4,minmax(0,1fr))}.property-offer-detail__grid{grid-template-columns:minmax(0,1.75fr) minmax(20rem,.9fr);align-items:start}}\n"] }]
|
|
4956
|
+
}], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], market: [{ type: i0.Input, args: [{ isSignal: true, alias: "market", required: false }] }], eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], backLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "backLabel", required: false }] }] } });
|
|
4960
4957
|
|
|
4961
|
-
class
|
|
4958
|
+
class ViewingDetailContext {
|
|
4962
4959
|
propertyService = inject(PropertyService);
|
|
4963
|
-
|
|
4964
|
-
viewing;
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
this.
|
|
4960
|
+
viewingId = signal(null, ...(ngDevMode ? [{ debugName: "viewingId" }] : []));
|
|
4961
|
+
viewing = signal(null, ...(ngDevMode ? [{ debugName: "viewing" }] : []));
|
|
4962
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
4963
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
4964
|
+
constructor() {
|
|
4965
|
+
effect(() => {
|
|
4966
|
+
const id = this.viewingId();
|
|
4967
|
+
if (!id) {
|
|
4968
|
+
return;
|
|
4969
|
+
}
|
|
4970
|
+
void this.reload(id);
|
|
4970
4971
|
});
|
|
4971
4972
|
}
|
|
4972
|
-
|
|
4973
|
-
this.
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4973
|
+
init(id) {
|
|
4974
|
+
if (this.viewingId() !== id) {
|
|
4975
|
+
this.viewing.set(null);
|
|
4976
|
+
}
|
|
4977
|
+
this.error.set(null);
|
|
4978
|
+
this.viewingId.set(id);
|
|
4979
|
+
}
|
|
4980
|
+
clear() {
|
|
4981
|
+
this.viewingId.set(null);
|
|
4982
|
+
this.viewing.set(null);
|
|
4983
|
+
this.loading.set(false);
|
|
4984
|
+
this.error.set(null);
|
|
4985
|
+
}
|
|
4986
|
+
updateViewing(updater) {
|
|
4987
|
+
const current = this.viewing();
|
|
4988
|
+
if (!current) {
|
|
4989
|
+
return;
|
|
4990
|
+
}
|
|
4991
|
+
this.viewing.set(updater(current));
|
|
4992
|
+
}
|
|
4993
|
+
async reload(id = this.viewingId()) {
|
|
4994
|
+
if (!id) {
|
|
4995
|
+
return;
|
|
4996
|
+
}
|
|
4997
|
+
this.loading.set(true);
|
|
4998
|
+
this.error.set(null);
|
|
4999
|
+
try {
|
|
5000
|
+
const response = await firstValueFrom(this.propertyService.getViewing(id));
|
|
5001
|
+
if (this.viewingId() !== id) {
|
|
5002
|
+
return;
|
|
5003
|
+
}
|
|
5004
|
+
this.viewing.set(response.data ?? response);
|
|
5005
|
+
}
|
|
5006
|
+
catch (error) {
|
|
5007
|
+
if (this.viewingId() === id) {
|
|
5008
|
+
this.error.set(this.errorMessage(error, 'Failed to load viewing detail.'));
|
|
5009
|
+
}
|
|
5010
|
+
console.error(error);
|
|
5011
|
+
}
|
|
5012
|
+
finally {
|
|
5013
|
+
if (this.viewingId() === id) {
|
|
5014
|
+
this.loading.set(false);
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
errorMessage(error, fallback) {
|
|
5019
|
+
return error?.error?.message ?? error?.message ?? fallback;
|
|
5020
|
+
}
|
|
5021
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5022
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext });
|
|
5023
|
+
}
|
|
5024
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: ViewingDetailContext, decorators: [{
|
|
5025
|
+
type: Injectable
|
|
5026
|
+
}], ctorParameters: () => [] });
|
|
5027
|
+
|
|
5028
|
+
class PropertyViewingFacade {
|
|
5029
|
+
propertyService = inject(PropertyService);
|
|
5030
|
+
snackBarService = inject(SnackBarService);
|
|
5031
|
+
ctx = inject(ViewingDetailContext);
|
|
5032
|
+
viewing = computed(() => this.ctx.viewing(), ...(ngDevMode ? [{ debugName: "viewing" }] : []));
|
|
5033
|
+
acceptingSlotId = signal(null, ...(ngDevMode ? [{ debugName: "acceptingSlotId" }] : []));
|
|
5034
|
+
cancelling = signal(false, ...(ngDevMode ? [{ debugName: "cancelling" }] : []));
|
|
5035
|
+
requiresSlotAcceptance = computed(() => {
|
|
5036
|
+
const viewing = this.viewing();
|
|
5037
|
+
return (!viewing?.viewingDate &&
|
|
5038
|
+
(viewing?.slotDecisionRequired === true ||
|
|
5039
|
+
this.slotProposalSource(viewing) === 'ADMIN_COUNTER' ||
|
|
5040
|
+
propertyViewingStatusCode(viewing?.status) === 'COUNTERED'));
|
|
5041
|
+
}, ...(ngDevMode ? [{ debugName: "requiresSlotAcceptance" }] : []));
|
|
5042
|
+
canCancel = computed(() => {
|
|
5043
|
+
const code = propertyViewingStatusCode(this.viewing()?.status);
|
|
5044
|
+
return code !== 'CANCELLED' && code !== 'COMPLETED';
|
|
5045
|
+
}, ...(ngDevMode ? [{ debugName: "canCancel" }] : []));
|
|
5046
|
+
canAcceptCounterSlot(slot) {
|
|
5047
|
+
return this.requiresSlotAcceptance() && !!slot?.id && this.acceptingSlotId() !== slot.id;
|
|
5048
|
+
}
|
|
5049
|
+
async cancel() {
|
|
5050
|
+
const viewingId = this.ctx.viewingId();
|
|
5051
|
+
if (!viewingId || !this.viewing() || !this.canCancel() || this.cancelling()) {
|
|
5052
|
+
return;
|
|
5053
|
+
}
|
|
5054
|
+
this.cancelling.set(true);
|
|
5055
|
+
try {
|
|
5056
|
+
await firstValueFrom(this.propertyService.cancelViewing(viewingId));
|
|
5057
|
+
this.ctx.updateViewing((viewing) => ({
|
|
5058
|
+
...viewing,
|
|
5059
|
+
status: 'CANCELLED',
|
|
5060
|
+
slotDecisionRequired: false,
|
|
5061
|
+
}));
|
|
5062
|
+
this.snackBarService.open('Cancelled');
|
|
5063
|
+
}
|
|
5064
|
+
catch (error) {
|
|
5065
|
+
this.snackBarService.open(this.errorMessage(error, 'Failed to cancel viewing.'));
|
|
5066
|
+
}
|
|
5067
|
+
finally {
|
|
5068
|
+
this.cancelling.set(false);
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
async acceptCounterSlot(slot) {
|
|
5072
|
+
const viewingId = this.ctx.viewingId();
|
|
5073
|
+
if (!viewingId) {
|
|
5074
|
+
return;
|
|
5075
|
+
}
|
|
5076
|
+
if (!slot?.id) {
|
|
5077
|
+
this.snackBarService.open('This slot cannot be accepted because the slot id is missing.');
|
|
5078
|
+
return;
|
|
5079
|
+
}
|
|
5080
|
+
this.acceptingSlotId.set(slot.id);
|
|
5081
|
+
try {
|
|
5082
|
+
const response = await firstValueFrom(this.propertyService.confirmViewing(viewingId, slot.id));
|
|
5083
|
+
const nextViewing = response?.data;
|
|
5084
|
+
this.ctx.updateViewing((viewing) => ({
|
|
5085
|
+
...viewing,
|
|
5086
|
+
...nextViewing,
|
|
5087
|
+
status: nextViewing?.status ?? 'APPROVED',
|
|
5088
|
+
viewingDate: nextViewing?.viewingDate ?? slot.date ?? viewing.viewingDate,
|
|
5089
|
+
viewingTime: nextViewing?.viewingTime ?? slot.time ?? viewing.viewingTime,
|
|
5090
|
+
proposedSlots: nextViewing?.proposedSlots ?? viewing.proposedSlots,
|
|
5091
|
+
item: nextViewing?.item ?? viewing.item,
|
|
5092
|
+
slotProposalSource: nextViewing?.slotProposalSource ?? viewing.slotProposalSource,
|
|
5093
|
+
slotDecisionRequired: nextViewing?.slotDecisionRequired ?? false,
|
|
5094
|
+
}));
|
|
5095
|
+
this.snackBarService.open('Viewing confirmed.');
|
|
5096
|
+
}
|
|
5097
|
+
catch (error) {
|
|
5098
|
+
this.snackBarService.open(this.errorMessage(error, 'Failed to confirm viewing.'));
|
|
5099
|
+
}
|
|
5100
|
+
finally {
|
|
5101
|
+
this.acceptingSlotId.set(null);
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
slotProposalSource(viewing) {
|
|
5105
|
+
const explicitSource = viewing?.slotProposalSource;
|
|
5106
|
+
if (explicitSource) {
|
|
5107
|
+
return explicitSource.toUpperCase();
|
|
5108
|
+
}
|
|
5109
|
+
const slotSource = viewing?.proposedSlots?.[0]?.source;
|
|
5110
|
+
return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
|
|
5111
|
+
}
|
|
5112
|
+
errorMessage(error, fallback) {
|
|
5113
|
+
return error?.error?.message ?? error?.message ?? fallback;
|
|
5114
|
+
}
|
|
5115
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5116
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade });
|
|
5117
|
+
}
|
|
5118
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingFacade, decorators: [{
|
|
5119
|
+
type: Injectable
|
|
5120
|
+
}] });
|
|
5121
|
+
|
|
5122
|
+
class PropertyViewingDetailComponent extends BaseComponent {
|
|
5123
|
+
ctx = inject(ViewingDetailContext);
|
|
5124
|
+
facade = inject(PropertyViewingFacade);
|
|
5125
|
+
viewing = computed(() => this.ctx.viewing(), ...(ngDevMode ? [{ debugName: "viewing" }] : []));
|
|
5126
|
+
applicantType = PropertyApplicantType;
|
|
5127
|
+
constructor() {
|
|
5128
|
+
super();
|
|
5129
|
+
effect(() => {
|
|
5130
|
+
this.titleService.setTitle(this.viewing()?.item?.title || 'Viewing detail');
|
|
4978
5131
|
});
|
|
4979
5132
|
}
|
|
4980
|
-
|
|
4981
|
-
this.
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
this.
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
},
|
|
5133
|
+
ngOnInit() {
|
|
5134
|
+
this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
|
|
5135
|
+
const id = params.get('id');
|
|
5136
|
+
if (!id) {
|
|
5137
|
+
this.ctx.clear();
|
|
5138
|
+
return;
|
|
5139
|
+
}
|
|
5140
|
+
this.ctx.init(id);
|
|
4989
5141
|
});
|
|
4990
5142
|
}
|
|
4991
5143
|
statusLabel(status) {
|
|
@@ -5004,10 +5156,10 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
5004
5156
|
}
|
|
5005
5157
|
}
|
|
5006
5158
|
statusHeadline() {
|
|
5007
|
-
if (this.requiresSlotAcceptance()) {
|
|
5159
|
+
if (this.facade.requiresSlotAcceptance()) {
|
|
5008
5160
|
return 'The listing team returned new slots for you to choose from.';
|
|
5009
5161
|
}
|
|
5010
|
-
switch (propertyViewingStatusCode(this.viewing?.status)) {
|
|
5162
|
+
switch (propertyViewingStatusCode(this.viewing()?.status)) {
|
|
5011
5163
|
case 'COUNTERED':
|
|
5012
5164
|
return 'The listing team returned new slots for you to choose from.';
|
|
5013
5165
|
case 'APPROVED':
|
|
@@ -5023,16 +5175,17 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
5023
5175
|
}
|
|
5024
5176
|
}
|
|
5025
5177
|
statusDescription() {
|
|
5026
|
-
|
|
5178
|
+
const viewing = this.viewing();
|
|
5179
|
+
if (!viewing) {
|
|
5027
5180
|
return '';
|
|
5028
5181
|
}
|
|
5029
|
-
if (this.requiresSlotAcceptance()) {
|
|
5182
|
+
if (this.facade.requiresSlotAcceptance()) {
|
|
5030
5183
|
return 'Review the three returned options below and accept the slot that works best for you.';
|
|
5031
5184
|
}
|
|
5032
5185
|
if (this.confirmedSlotLabel()) {
|
|
5033
5186
|
return `Confirmed for ${this.confirmedSlotLabel()}.`;
|
|
5034
5187
|
}
|
|
5035
|
-
switch (propertyViewingStatusCode(
|
|
5188
|
+
switch (propertyViewingStatusCode(viewing.status)) {
|
|
5036
5189
|
case 'REJECTED':
|
|
5037
5190
|
return 'Review the proposed slots and submit a new request if you still want to visit the property.';
|
|
5038
5191
|
case 'CANCELLED':
|
|
@@ -5044,34 +5197,38 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
5044
5197
|
}
|
|
5045
5198
|
}
|
|
5046
5199
|
confirmedSlotLabel() {
|
|
5047
|
-
|
|
5200
|
+
const viewing = this.viewing();
|
|
5201
|
+
if (!viewing?.viewingDate) {
|
|
5048
5202
|
return null;
|
|
5049
5203
|
}
|
|
5050
|
-
const date = new Date(
|
|
5204
|
+
const date = new Date(viewing.viewingDate);
|
|
5051
5205
|
const hasValidDate = !Number.isNaN(date.getTime());
|
|
5052
5206
|
const formattedDate = hasValidDate
|
|
5053
5207
|
? new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium' }).format(date)
|
|
5054
|
-
:
|
|
5055
|
-
return
|
|
5208
|
+
: viewing.viewingDate;
|
|
5209
|
+
return viewing.viewingTime ? `${formattedDate} at ${viewing.viewingTime}` : formattedDate;
|
|
5056
5210
|
}
|
|
5057
5211
|
isConfirmedSlot(slot) {
|
|
5058
|
-
|
|
5212
|
+
const viewing = this.viewing();
|
|
5213
|
+
return !!viewing?.viewingDate && slot.date === viewing.viewingDate && slot.time === viewing.viewingTime;
|
|
5059
5214
|
}
|
|
5060
5215
|
primaryMediaUrl() {
|
|
5061
|
-
return this.viewing?.item?.media?.[0]?.url || '';
|
|
5216
|
+
return this.viewing()?.item?.media?.[0]?.url || '';
|
|
5062
5217
|
}
|
|
5063
5218
|
requesterName() {
|
|
5064
|
-
const
|
|
5219
|
+
const viewing = this.viewing();
|
|
5220
|
+
const fullName = `${viewing?.firstName ?? ''} ${viewing?.lastName ?? ''}`.trim();
|
|
5065
5221
|
return fullName || 'Viewer';
|
|
5066
5222
|
}
|
|
5067
5223
|
contactSummary() {
|
|
5068
|
-
|
|
5224
|
+
const viewing = this.viewing();
|
|
5225
|
+
if (!viewing) {
|
|
5069
5226
|
return 'Contact details available below';
|
|
5070
5227
|
}
|
|
5071
|
-
if (
|
|
5072
|
-
return `${
|
|
5228
|
+
if (viewing.email && viewing.phone) {
|
|
5229
|
+
return `${viewing.email} · ${viewing.phone}`;
|
|
5073
5230
|
}
|
|
5074
|
-
return
|
|
5231
|
+
return viewing.email || viewing.phone || 'Contact details available below';
|
|
5075
5232
|
}
|
|
5076
5233
|
viewerCategoryLabel(value) {
|
|
5077
5234
|
if (!value) {
|
|
@@ -5093,50 +5250,6 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
5093
5250
|
requesterTypeLabel(value) {
|
|
5094
5251
|
return value ? this.humanize(value) : 'Direct request';
|
|
5095
5252
|
}
|
|
5096
|
-
requiresSlotAcceptance() {
|
|
5097
|
-
return (!this.viewing?.viewingDate &&
|
|
5098
|
-
(this.viewing?.slotDecisionRequired === true ||
|
|
5099
|
-
this.slotProposalSource() === 'ADMIN_COUNTER' ||
|
|
5100
|
-
propertyViewingStatusCode(this.viewing?.status) === 'COUNTERED'));
|
|
5101
|
-
}
|
|
5102
|
-
canAcceptCounterSlot(slot) {
|
|
5103
|
-
return this.requiresSlotAcceptance() && !!slot?.id && this.acceptingSlotId() !== slot.id;
|
|
5104
|
-
}
|
|
5105
|
-
acceptCounterSlot(slot) {
|
|
5106
|
-
if (!slot?.id) {
|
|
5107
|
-
this.snackBarService.open('This slot cannot be accepted because the slot id is missing.');
|
|
5108
|
-
return;
|
|
5109
|
-
}
|
|
5110
|
-
this.acceptingSlotId.set(slot.id);
|
|
5111
|
-
this.propertyService.confirmViewing(this.id, slot.id).subscribe({
|
|
5112
|
-
next: (res) => {
|
|
5113
|
-
const nextViewing = res?.data;
|
|
5114
|
-
this.viewing = {
|
|
5115
|
-
...this.viewing,
|
|
5116
|
-
...nextViewing,
|
|
5117
|
-
status: nextViewing?.status ?? 'APPROVED',
|
|
5118
|
-
viewingDate: nextViewing?.viewingDate ?? slot.date ?? this.viewing.viewingDate,
|
|
5119
|
-
viewingTime: nextViewing?.viewingTime ?? slot.time ?? this.viewing.viewingTime,
|
|
5120
|
-
proposedSlots: nextViewing?.proposedSlots ?? this.viewing.proposedSlots,
|
|
5121
|
-
item: nextViewing?.item ?? this.viewing.item,
|
|
5122
|
-
slotProposalSource: nextViewing?.slotProposalSource ?? this.viewing.slotProposalSource,
|
|
5123
|
-
slotDecisionRequired: nextViewing?.slotDecisionRequired ?? false,
|
|
5124
|
-
};
|
|
5125
|
-
this.snackBarService.open('Viewing confirmed.');
|
|
5126
|
-
},
|
|
5127
|
-
error: (error) => {
|
|
5128
|
-
this.acceptingSlotId.set(null);
|
|
5129
|
-
this.snackBarService.open(error.message);
|
|
5130
|
-
},
|
|
5131
|
-
complete: () => {
|
|
5132
|
-
this.acceptingSlotId.set(null);
|
|
5133
|
-
},
|
|
5134
|
-
});
|
|
5135
|
-
}
|
|
5136
|
-
canCancel() {
|
|
5137
|
-
const code = propertyViewingStatusCode(this.viewing?.status);
|
|
5138
|
-
return code !== 'CANCELLED' && code !== 'COMPLETED';
|
|
5139
|
-
}
|
|
5140
5253
|
humanize(value) {
|
|
5141
5254
|
return value
|
|
5142
5255
|
.trim()
|
|
@@ -5144,16 +5257,8 @@ class PropertyViewingDetailComponent extends BaseComponent {
|
|
|
5144
5257
|
.toLowerCase()
|
|
5145
5258
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
5146
5259
|
}
|
|
5147
|
-
|
|
5148
|
-
const explicitSource = this.viewing?.slotProposalSource;
|
|
5149
|
-
if (explicitSource) {
|
|
5150
|
-
return explicitSource.toUpperCase();
|
|
5151
|
-
}
|
|
5152
|
-
const slotSource = this.viewing?.proposedSlots?.[0]?.source;
|
|
5153
|
-
return slotSource ? slotSource.toUpperCase() : 'REQUESTER';
|
|
5154
|
-
}
|
|
5155
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
5156
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", usesInheritance: true, ngImport: i0, template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"acceptingSlotId() === slot.id\"\n (click)=\"acceptCounterSlot(slot)\"\n >\n @if (acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
5260
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5261
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", providers: [ViewingDetailContext, PropertyViewingFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing() ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing() ? statusLabel(viewing()?.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing()?.item?.propertyId; as propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', propertyId]\" i18n>Open property</a>\n }\n @if (viewing() && facade.canCancel()) {\n <button mat-flat-button type=\"button\" [disabled]=\"facade.cancelling()\" (click)=\"facade.cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing(); as viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (facade.requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (facade.requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!facade.canAcceptCounterSlot(slot)\"\n (click)=\"facade.acceptCounterSlot(slot)\"\n >\n @if (facade.acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (facade.requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
5157
5262
|
}
|
|
5158
5263
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyViewingDetailComponent, decorators: [{
|
|
5159
5264
|
type: Component,
|
|
@@ -5167,8 +5272,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
5167
5272
|
ToolbarComponent,
|
|
5168
5273
|
RouterLink,
|
|
5169
5274
|
PricePipe,
|
|
5170
|
-
], template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing ? statusLabel(viewing.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Open property</a>\n }\n @if (viewing && canCancel()) {\n <button mat-flat-button type=\"button\" (click)=\"cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"acceptingSlotId() === slot.id\"\n (click)=\"acceptCounterSlot(slot)\"\n >\n @if (acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"] }]
|
|
5171
|
-
}] });
|
|
5275
|
+
], providers: [ViewingDetailContext, PropertyViewingFacade], template: "<div class=\"property-viewing-detail xl:max-w-[1024px] 2xl:max-w-[1280px] m-auto\">\n <rolatech-toolbar\n [title]=\"viewing() ? 'Viewing detail' : 'Loading viewing'\"\n [subtitle]=\"viewing() ? statusLabel(viewing()?.status) : ''\"\n large\n link=\"../\"\n >\n @if (viewing()?.item?.propertyId; as propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', propertyId]\" i18n>Open property</a>\n }\n @if (viewing() && facade.canCancel()) {\n <button mat-flat-button type=\"button\" [disabled]=\"facade.cancelling()\" (click)=\"facade.cancel()\" i18n>Cancel request</button>\n }\n </rolatech-toolbar>\n\n @if (viewing(); as viewing) {\n <section class=\"property-viewing-detail__hero\">\n <div class=\"property-viewing-detail__hero-copy\">\n <span class=\"property-viewing-detail__eyebrow\" i18n>Viewing request</span>\n <div class=\"property-viewing-detail__hero-heading\">\n <h1 class=\"property-viewing-detail__title\">{{ viewing.item.title }}</h1>\n <span [class]=\"statusToneClass(viewing.status)\">{{ statusLabel(viewing.status) }}</span>\n </div>\n\n @if (viewing.item.address) {\n <p class=\"property-viewing-detail__address\">{{ viewing.item.address }}</p>\n }\n\n <p class=\"property-viewing-detail__intro\">{{ statusHeadline() }}</p>\n <p class=\"property-viewing-detail__summary-note\">{{ statusDescription() }}</p>\n\n <div class=\"property-viewing-detail__fact-grid\">\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Confirmed slot</span>\n <strong class=\"property-viewing-detail__fact-value\">{{ confirmedSlotLabel() || 'Awaiting confirmation' }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Move-in target</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.moveInDate ? (viewing.moveInDate | date: 'mediumDate') : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Monthly budget</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.monthlyBudget ? (viewing.monthlyBudget | price) : 'Not shared'\n }}</strong>\n </article>\n <article class=\"property-viewing-detail__fact\">\n <span class=\"property-viewing-detail__fact-label\" i18n>Annual income</span>\n <strong class=\"property-viewing-detail__fact-value\">{{\n viewing.annualIncome ? (viewing.annualIncome | price) : 'Not shared'\n }}</strong>\n </article>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__hero-side\">\n <div class=\"property-viewing-detail__property-card\" [routerLink]=\"['../../', viewing.item.propertyId]\">\n <div class=\"property-viewing-detail__media\">\n @if (primaryMediaUrl()) {\n @defer {\n <rolatech-thumbnail [src]=\"primaryMediaUrl()\" size=\"medium\" mode=\"clip\"></rolatech-thumbnail>\n } @placeholder {\n <div class=\"property-viewing-detail__media-fallback\"></div>\n }\n } @else {\n <rolatech-image-placeholder></rolatech-image-placeholder>\n }\n </div>\n\n <div class=\"property-viewing-detail__property-copy\">\n <div class=\"property-viewing-detail__property-price\">{{ viewing.item.price | price }}</div>\n <div class=\"property-viewing-detail__property-facts\">\n <span>{{ viewing.item.bedrooms }} bed</span>\n <span>{{ viewing.item.bathrooms }} bath</span>\n <span>{{ viewing.item.receptions }} reception</span>\n </div>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__summary-card\">\n <span class=\"property-viewing-detail__summary-label\" i18n>Requested by</span>\n <strong class=\"property-viewing-detail__summary-value\">{{ requesterName() }}</strong>\n <span class=\"property-viewing-detail__summary-contact\">{{ contactSummary() }}</span>\n </div>\n </div>\n </section>\n\n <div class=\"property-viewing-detail__content\">\n <main class=\"property-viewing-detail__main\">\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Viewer profile</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Contact and application details</h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__info-grid\">\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Name</span>\n <strong>{{ viewing.firstName }} {{ viewing.lastName }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Email</span>\n <strong>{{ viewing.email || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Phone</span>\n <strong>{{ viewing.phone || 'Not shared' }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Viewer type</span>\n <strong>{{ viewerCategoryLabel(viewing.viewerCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Applicant type</span>\n <strong>{{ applicantTypeLabel(viewing.tenantCategory) }}</strong>\n </article>\n <article class=\"property-viewing-detail__info-card\">\n <span class=\"property-viewing-detail__info-label\" i18n>Job title</span>\n <strong>{{ viewing.jobTitle || 'Not shared' }}</strong>\n </article>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel\">\n <div class=\"property-viewing-detail__panel-head\">\n <div>\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Preferred slots</span>\n <h2 class=\"property-viewing-detail__panel-title\">\n @if (facade.requiresSlotAcceptance()) {\n Choose one of the returned slots\n } @else {\n Availability shared with the listing team\n }\n </h2>\n </div>\n </div>\n\n <div class=\"property-viewing-detail__slot-list\">\n @for (slot of viewing.proposedSlots; track slot.id || $index + ':' + slot.date + ':' + slot.time) {\n <article\n class=\"property-viewing-detail__slot-card\"\n [class.property-viewing-detail__slot-card--confirmed]=\"isConfirmedSlot(slot)\"\n >\n <div class=\"property-viewing-detail__slot-copy\">\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>calendar_today</mat-icon>\n <strong>{{ slot.date | date: 'mediumDate' }}</strong>\n </div>\n <div class=\"property-viewing-detail__slot-line\">\n <mat-icon>schedule</mat-icon>\n <span>{{ slot.time }}</span>\n </div>\n </div>\n\n @if (isConfirmedSlot(slot)) {\n <span class=\"property-viewing-detail__slot-state property-viewing-detail__slot-state--confirmed\" i18n\n >Confirmed</span\n >\n } @else if (facade.requiresSlotAcceptance() && slot.id) {\n <button\n mat-flat-button\n type=\"button\"\n [disabled]=\"!facade.canAcceptCounterSlot(slot)\"\n (click)=\"facade.acceptCounterSlot(slot)\"\n >\n @if (facade.acceptingSlotId() === slot.id) {\n Accepting...\n } @else {\n Accept this slot\n }\n </button>\n } @else {\n <span class=\"property-viewing-detail__slot-state\" i18n>Proposed</span>\n }\n </article>\n } @empty {\n <div class=\"property-viewing-detail__empty\" i18n>No proposed slots were attached to this request.</div>\n }\n </div>\n </section>\n </main>\n\n <aside class=\"property-viewing-detail__side\">\n <div class=\"property-viewing-detail__sticky\">\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Request context</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>How this enquiry was submitted</h2>\n\n <div class=\"property-viewing-detail__summary-list\">\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester type</span>\n <strong>{{ requesterTypeLabel(viewing.requesterType) }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester app</span>\n <strong>{{ viewing.requesterAppId || viewing.appId || 'Not attached' }}</strong>\n </div>\n <div>\n <span class=\"property-viewing-detail__summary-key\" i18n>Requester org</span>\n <strong>{{ viewing.requesterOrgId || viewing.orgId || 'Not attached' }}</strong>\n </div>\n </div>\n </section>\n\n <section class=\"property-viewing-detail__panel property-viewing-detail__panel--subtle\">\n <span class=\"property-viewing-detail__panel-eyebrow\" i18n>Next step</span>\n <h2 class=\"property-viewing-detail__panel-title\" i18n>Keep this request moving</h2>\n <p class=\"property-viewing-detail__summary-note\">\n @if (facade.requiresSlotAcceptance()) {\n Accept the slot that works for you. As soon as you do, the selected appointment becomes the confirmed viewing.\n } @else {\n The listing team will confirm one of the proposed slots or follow up with a new time if availability changes.\n }\n </p>\n\n <div class=\"property-viewing-detail__journey-actions\">\n @if (viewing.item.propertyId) {\n <a mat-stroked-button [routerLink]=\"['../../', viewing.item.propertyId]\" i18n>Return to property</a>\n }\n <a mat-stroked-button routerLink=\"../\" i18n>Back to viewings</a>\n </div>\n </section>\n </div>\n </aside>\n </div>\n } @else {\n <section class=\"property-viewing-detail__hero property-viewing-detail__hero--loading\">\n <div class=\"property-viewing-detail__hero-copy\">\n <rolatech-skeleton class=\"h-5 w-28\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-10 w-3/4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-5 w-full\"></rolatech-skeleton>\n <div class=\"property-viewing-detail__fact-grid\">\n @for (fact of [0, 1, 2, 3]; track fact) {\n <article class=\"property-viewing-detail__fact\">\n <rolatech-skeleton class=\"h-3 w-24\"></rolatech-skeleton>\n <rolatech-skeleton class=\"h-6 w-32\"></rolatech-skeleton>\n </article>\n }\n </div>\n </div>\n <div class=\"property-viewing-detail__hero-side\">\n <article class=\"property-viewing-detail__property-card property-viewing-detail__property-card--loading\">\n <rolatech-skeleton class=\"h-full w-full\"></rolatech-skeleton>\n </article>\n </div>\n </section>\n }\n</div>\n", styles: [":host{display:block}.property-viewing-detail{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 32%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-viewing-detail__hero{display:grid;gap:1rem;padding:1.25rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.75rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 95%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff);box-shadow:0 24px 55px -42px color-mix(in srgb,var(--rt-text-primary) 24%,transparent)}.property-viewing-detail__hero--loading{grid-template-columns:1fr}.property-viewing-detail__hero-copy,.property-viewing-detail__hero-side,.property-viewing-detail__main,.property-viewing-detail__side,.property-viewing-detail__sticky{height:fit-content;display:flex;flex-direction:column;gap:1rem}.property-viewing-detail__eyebrow,.property-viewing-detail__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-viewing-detail__hero-heading{display:flex;flex-direction:column;gap:.75rem}.property-viewing-detail__title{margin:0;color:var(--rt-text-primary);font-size:clamp(2rem,3vw,3rem);line-height:1.05;font-weight:800}.property-viewing-detail__address,.property-viewing-detail__intro,.property-viewing-detail__summary-note{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-viewing-detail__address{font-size:1rem;font-weight:600}.property-viewing-detail__intro{color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-viewing-detail__status{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.35rem .85rem;font-size:.82rem;font-weight:700}.property-viewing-detail__status--positive{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__status--negative{background:color-mix(in srgb,#dc2626 14%,transparent);color:#991b1b}.property-viewing-detail__status--neutral{background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary)}.property-viewing-detail__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__fact,.property-viewing-detail__summary-card,.property-viewing-detail__info-card,.property-viewing-detail__slot-card{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 90%,var(--rt-brand-color) 10%)}.property-viewing-detail__fact{display:flex;flex-direction:column;gap:.3rem;padding:.95rem 1rem}.property-viewing-detail__fact-label,.property-viewing-detail__summary-label,.property-viewing-detail__info-label,.property-viewing-detail__summary-key{color:var(--rt-text-secondary);font-size:.84rem}.property-viewing-detail__fact-value,.property-viewing-detail__summary-value{color:var(--rt-text-primary);font-size:1.15rem;font-weight:700}.property-viewing-detail__property-card{display:flex;flex-direction:column;gap:.95rem;min-height:100%;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent);cursor:pointer}.property-viewing-detail__property-card--loading{min-height:18rem}.property-viewing-detail__media{overflow:hidden;border-radius:1.2rem;aspect-ratio:16/10;background:color-mix(in srgb,var(--rt-10-percent-layer, transparent) 75%,transparent)}.property-viewing-detail__media :is(rolatech-thumbnail,rolatech-image-placeholder,img){display:block;width:100%;height:100%}.property-viewing-detail__media-fallback{width:100%;height:100%;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-brand-color) 12%,transparent),transparent 58%),color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,transparent)}.property-viewing-detail__property-copy{display:flex;flex-direction:column;gap:.5rem}.property-viewing-detail__property-price{color:var(--rt-text-primary);font-size:1.55rem;font-weight:800}.property-viewing-detail__property-facts{display:flex;flex-wrap:wrap;gap:.6rem;color:var(--rt-text-secondary);font-size:.92rem}.property-viewing-detail__summary-card{display:flex;flex-direction:column;gap:.4rem;min-width:0;padding:1rem 1.05rem}.property-viewing-detail__summary-card>*{min-width:0}.property-viewing-detail__content{display:grid;gap:1rem}.property-viewing-detail__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;padding:1.25rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-viewing-detail__panel--subtle{background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-10-percent-layer, transparent) 8%)}.property-viewing-detail__panel-head{display:flex;flex-direction:column;gap:.85rem;margin-bottom:1rem}.property-viewing-detail__panel-title{margin:.4rem 0 0;color:var(--rt-text-primary);font-size:1.35rem;font-weight:700}.property-viewing-detail__info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-viewing-detail__info-card{display:flex;flex-direction:column;gap:.28rem;padding:.95rem 1rem}.property-viewing-detail__info-card strong,.property-viewing-detail__summary-list strong{color:var(--rt-text-primary);font-weight:700}.property-viewing-detail__summary-value,.property-viewing-detail__summary-contact{overflow-wrap:anywhere;word-break:break-word}.property-viewing-detail__summary-contact{color:var(--rt-text-secondary);line-height:1.6}.property-viewing-detail__slot-list,.property-viewing-detail__summary-list{display:flex;flex-direction:column;gap:.85rem}.property-viewing-detail__slot-card{display:flex;justify-content:space-between;gap:1rem;padding:1rem 1.05rem}.property-viewing-detail__slot-card--confirmed{border-color:color-mix(in srgb,#16a34a 28%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,#16a34a 8%,var(--rt-raised-background, #ffffff) 92%)}.property-viewing-detail__slot-copy{display:flex;flex-direction:column;gap:.45rem}.property-viewing-detail__slot-line{display:inline-flex;align-items:center;gap:.5rem;color:var(--rt-text-primary)}.property-viewing-detail__slot-line mat-icon{font-size:1rem;width:1rem;height:1rem;color:var(--rt-text-secondary)}.property-viewing-detail__slot-state{display:inline-flex;align-self:flex-start;justify-content:center;align-items:center;min-height:2rem;border-radius:9999px;padding:.3rem .8rem;background:color-mix(in srgb,var(--rt-text-primary) 10%,transparent);color:var(--rt-text-primary);font-size:.8rem;font-weight:700}.property-viewing-detail__slot-state--confirmed{background:color-mix(in srgb,#16a34a 16%,transparent);color:#166534}.property-viewing-detail__summary-list>div{display:flex;flex-direction:column;gap:.28rem}.property-viewing-detail__journey-actions{display:flex;flex-wrap:wrap;gap:.75rem;margin-top:1rem}.property-viewing-detail__journey-actions>*{min-height:2.85rem;border-radius:9999px}.property-viewing-detail__empty{padding:1rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .12));border-radius:1.25rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-viewing-detail{padding:1.25rem}.property-viewing-detail__hero{grid-template-columns:minmax(0,1.55fr) minmax(17rem,.9fr);align-items:stretch;padding:1.5rem}.property-viewing-detail__hero-heading,.property-viewing-detail__panel-head{flex-direction:row;justify-content:space-between;align-items:flex-start}}@media(min-width:1100px){.property-viewing-detail__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}.property-viewing-detail__sticky{position:sticky;top:1rem}}@media(max-width:767px){.property-viewing-detail__fact-grid,.property-viewing-detail__info-grid{grid-template-columns:1fr}.property-viewing-detail__slot-card{flex-direction:column}}\n"] }]
|
|
5276
|
+
}], ctorParameters: () => [] });
|
|
5172
5277
|
|
|
5173
5278
|
class PropertyWishlistComponent extends BaseComponent {
|
|
5174
5279
|
propertyService = inject(PropertyService);
|
|
@@ -7079,7 +7184,7 @@ class PropertyOfferCreate {
|
|
|
7079
7184
|
}
|
|
7080
7185
|
}
|
|
7081
7186
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7082
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? '
|
|
7187
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Market workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "component", type: PropertyIntentShell, selector: "rolatech-property-intent-shell", inputs: ["property"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }] });
|
|
7083
7188
|
}
|
|
7084
7189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyOfferCreate, decorators: [{
|
|
7085
7190
|
type: Component,
|
|
@@ -7090,7 +7195,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
7090
7195
|
PropertyOfferSaleForm,
|
|
7091
7196
|
PropertyIntentShell,
|
|
7092
7197
|
PropertyMarketBreadcrumbComponent,
|
|
7093
|
-
], providers: [OfferFormFacade], template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? '
|
|
7198
|
+
], providers: [OfferFormFacade], template: "<rolatech-property-intent-shell [property]=\"property()\">\n <div intent-header class=\"property-offer-create__header\">\n <rolatech-property-market-breadcrumb\n class=\"property-offer-create__breadcrumb\"\n [items]=\"marketBreadcrumbs()\"\n ></rolatech-property-market-breadcrumb>\n <span class=\"property-offer-create__eyebrow\">\n {{ requestFlow === 'agent' ? 'Agent workflow' : isMarketFlow() ? 'Market workflow' : 'Property offer' }}\n </span>\n <div class=\"property-offer-create__title\">Create {{ offerTypeLabel() }}</div>\n <p class=\"property-offer-create__description\">\n Share the core terms up front and the listing team can review the offer without switching between multiple forms.\n </p>\n </div>\n\n <div intent-body class=\"property-offer-create__body\">\n <div class=\"content\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"flex flex-col md:flex-row gap-3\">\n <button class=\"w-full md:w-2/3\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{ facade.submitting() ? 'Submitting' : 'Submit' }}\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n</rolatech-property-intent-shell>\n", styles: [".property-offer-create__header,.property-offer-create__breadcrumb{margin-bottom:1rem}.property-offer-create__eyebrow{display:inline-flex;margin-bottom:.85rem;border-radius:9999px;padding:.35rem .8rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-offer-create__title{color:var(--rt-text-primary);font-size:1.75rem;font-weight:700;line-height:1.15}.property-offer-create__description{margin:.75rem 0 0;color:var(--rt-text-secondary);line-height:1.7}.property-offer-create__body{display:flex;flex-direction:column;gap:1rem}.error{margin:0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}\n"] }]
|
|
7094
7199
|
}], ctorParameters: () => [] });
|
|
7095
7200
|
|
|
7096
7201
|
const propertyRoutes = [
|
|
@@ -7100,7 +7205,7 @@ const propertyRoutes = [
|
|
|
7100
7205
|
children: [
|
|
7101
7206
|
{
|
|
7102
7207
|
path: '',
|
|
7103
|
-
loadComponent: () => import('./rolatech-angular-property-property-index.component-
|
|
7208
|
+
loadComponent: () => import('./rolatech-angular-property-property-index.component-2BXmI0uJ.mjs').then((x) => x.PropertyIndexComponent),
|
|
7104
7209
|
},
|
|
7105
7210
|
],
|
|
7106
7211
|
},
|
|
@@ -9262,7 +9367,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9262
9367
|
const propertyManageViewingsRoutes = [
|
|
9263
9368
|
{
|
|
9264
9369
|
path: '',
|
|
9265
|
-
loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-
|
|
9370
|
+
loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-Cws6ElRk.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
|
|
9266
9371
|
},
|
|
9267
9372
|
{
|
|
9268
9373
|
path: ':id',
|
|
@@ -9311,19 +9416,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9311
9416
|
args: ['class.rolatech-property-market-item']
|
|
9312
9417
|
}], letting: [{ type: i0.Input, args: [{ isSignal: true, alias: "letting", required: true }] }], thumbnail: [{ type: i0.Input, args: [{ isSignal: true, alias: "thumbnail", required: false }] }], list: [{ type: i0.Input, args: [{ isSignal: true, alias: "list", required: false }] }] } });
|
|
9313
9418
|
|
|
9314
|
-
class PropertyWorkspaceShell {
|
|
9315
|
-
eyebrow = input('Workspace', ...(ngDevMode ? [{ debugName: "eyebrow" }] : []));
|
|
9316
|
-
title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
9317
|
-
description = input('', ...(ngDevMode ? [{ debugName: "description" }] : []));
|
|
9318
|
-
stats = input([], ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
9319
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9320
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyWorkspaceShell, isStandalone: true, selector: "rolatech-property-workspace-shell", inputs: { eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, stats: { classPropertyName: "stats", publicName: "stats", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
9321
|
-
}
|
|
9322
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyWorkspaceShell, decorators: [{
|
|
9323
|
-
type: Component,
|
|
9324
|
-
args: [{ selector: 'rolatech-property-workspace-shell', imports: [CommonModule], template: "<div class=\"property-workspace-shell\">\n <section class=\"property-workspace-shell__hero\">\n <div class=\"property-workspace-shell__copy\">\n <span class=\"property-workspace-shell__eyebrow\">{{ eyebrow() }}</span>\n <h1 class=\"property-workspace-shell__title\">{{ title() }}</h1>\n @if (description()) {\n <p class=\"property-workspace-shell__description\">{{ description() }}</p>\n }\n </div>\n\n @if (stats().length) {\n <div class=\"property-workspace-shell__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"property-workspace-shell__stat\">\n <span class=\"property-workspace-shell__stat-value\">{{ stat.value }}</span>\n <span class=\"property-workspace-shell__stat-label\">{{ stat.label }}</span>\n @if (stat.hint) {\n <span class=\"property-workspace-shell__stat-hint\">{{ stat.hint }}</span>\n }\n </article>\n }\n </div>\n }\n </section>\n\n <div class=\"property-workspace-shell__actions\">\n <ng-content select=\"[workspace-actions]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__filters\">\n <ng-content select=\"[workspace-filters]\"></ng-content>\n </div>\n\n <div class=\"property-workspace-shell__body\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [":host{display:block}.property-workspace-shell{display:flex;flex-direction:column;gap:1rem}.property-workspace-shell__hero,.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.property-workspace-shell__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-workspace-shell__copy{display:flex;flex-direction:column;gap:.75rem}.property-workspace-shell__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-workspace-shell__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.property-workspace-shell__description{margin:0;max-width:56rem;color:var(--rt-text-secondary);line-height:1.7}.property-workspace-shell__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-workspace-shell__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-workspace-shell__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.property-workspace-shell__stat-label,.property-workspace-shell__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.property-workspace-shell__actions,.property-workspace-shell__filters,.property-workspace-shell__body{padding:1rem 1.1rem}.property-workspace-shell__actions:empty,.property-workspace-shell__filters:empty{display:none}@media(min-width:960px){.property-workspace-shell__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}}\n"] }]
|
|
9325
|
-
}], propDecorators: { eyebrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "eyebrow", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], stats: [{ type: i0.Input, args: [{ isSignal: true, alias: "stats", required: false }] }] } });
|
|
9326
|
-
|
|
9327
9419
|
class PropertyMarketIndex extends BaseComponent {
|
|
9328
9420
|
propertySearchService = inject(PropertySearchService);
|
|
9329
9421
|
loading = false;
|
|
@@ -9798,10 +9890,22 @@ class PropertyMarketViewingIndex extends BaseComponent {
|
|
|
9798
9890
|
stats = computed(() => {
|
|
9799
9891
|
const viewings = this.viewings();
|
|
9800
9892
|
return [
|
|
9801
|
-
{ label: '
|
|
9802
|
-
{
|
|
9803
|
-
|
|
9804
|
-
|
|
9893
|
+
{ label: 'Requests', value: this.length || viewings.length, hint: 'Visits requested while you are acting as a buyer' },
|
|
9894
|
+
{
|
|
9895
|
+
label: 'Pending',
|
|
9896
|
+
value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'PENDING').length,
|
|
9897
|
+
hint: 'Waiting for listing-side review',
|
|
9898
|
+
},
|
|
9899
|
+
{
|
|
9900
|
+
label: 'Approved',
|
|
9901
|
+
value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'APPROVED').length,
|
|
9902
|
+
hint: 'Confirmed appointments in your buyer workspace',
|
|
9903
|
+
},
|
|
9904
|
+
{
|
|
9905
|
+
label: 'Completed',
|
|
9906
|
+
value: viewings.filter((item) => propertyViewingStatusCode(item.status) === 'COMPLETED').length,
|
|
9907
|
+
hint: 'Finished visits linked to saved market activity',
|
|
9908
|
+
},
|
|
9805
9909
|
];
|
|
9806
9910
|
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
9807
9911
|
ngOnInit() {
|
|
@@ -9850,7 +9954,7 @@ class PropertyMarketViewingIndex extends BaseComponent {
|
|
|
9850
9954
|
});
|
|
9851
9955
|
}
|
|
9852
9956
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9853
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingIndex, isStandalone: true, selector: "rolatech-property-market-viewing-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"
|
|
9957
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingIndex, isStandalone: true, selector: "rolatech-property-market-viewing-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"workspace\"\n title=\"viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n } } @else if (viewings().length) { @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }] });
|
|
9854
9958
|
}
|
|
9855
9959
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingIndex, decorators: [{
|
|
9856
9960
|
type: Component,
|
|
@@ -9861,7 +9965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9861
9965
|
PropertyViewingItemComponent,
|
|
9862
9966
|
MatPaginatorModule,
|
|
9863
9967
|
PropertyWorkspaceShell,
|
|
9864
|
-
], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"
|
|
9968
|
+
], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"workspace\"\n title=\"viewing requests\"\n description=\"Track every market-side viewing request you made as an agent buyer, keep approval changes visible, and jump back into the property journey when needed.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-viewing-index__status-nav\" aria-label=\"viewing status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-viewing-index__status-link\"\n [class.property-market-viewing-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-viewing-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <div class=\"property-market-viewing-index__placeholder\"></div>\n } } @else if (viewings().length) { @for (item of viewings(); track item.id) {\n <rolatech-property-viewing-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <div class=\"property-market-viewing-index__empty\">\n <h3>No buyer viewings yet</h3>\n <p>Your market-side viewing requests will appear here once you start booking visits as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-viewing-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-viewing-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-viewing-index__status-link:hover,.property-market-viewing-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-viewing-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-viewing-index__placeholder,.property-market-viewing-index__empty{min-height:10rem;border-radius:1.35rem}.property-market-viewing-index__placeholder{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));background:linear-gradient(90deg,color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent),transparent),color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent)}.property-market-viewing-index__empty{display:flex;flex-direction:column;justify-content:center;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-viewing-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-viewing-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
|
|
9865
9969
|
}] });
|
|
9866
9970
|
|
|
9867
9971
|
class PropertyMarketViewingDetail {
|
|
@@ -9880,10 +9984,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9880
9984
|
|
|
9881
9985
|
class PropertyMarketOfferIndex extends BaseComponent {
|
|
9882
9986
|
propertyOfferService = inject(PropertyOfferService);
|
|
9883
|
-
breadcrumbs = [
|
|
9884
|
-
{ label: 'Properties', link: '/properties' },
|
|
9885
|
-
{ label: 'Offers' },
|
|
9886
|
-
];
|
|
9987
|
+
breadcrumbs = [{ label: 'Properties', link: '/properties' }, { label: 'Offers' }];
|
|
9887
9988
|
select = 0;
|
|
9888
9989
|
links = [
|
|
9889
9990
|
{ name: 'All', status: '' },
|
|
@@ -9902,10 +10003,22 @@ class PropertyMarketOfferIndex extends BaseComponent {
|
|
|
9902
10003
|
stats = computed(() => {
|
|
9903
10004
|
const offers = this.offers();
|
|
9904
10005
|
return [
|
|
9905
|
-
{ label: '
|
|
9906
|
-
{
|
|
9907
|
-
|
|
9908
|
-
|
|
10006
|
+
{ label: 'Offers', value: this.length || offers.length, hint: 'Offers you submitted while acting as a buyer agent' },
|
|
10007
|
+
{
|
|
10008
|
+
label: 'Submitted',
|
|
10009
|
+
value: offers.filter((item) => item.status === PropertyOfferStatus.SUBMITTED).length,
|
|
10010
|
+
hint: 'Waiting for listing-side review or response',
|
|
10011
|
+
},
|
|
10012
|
+
{
|
|
10013
|
+
label: 'Accepted',
|
|
10014
|
+
value: offers.filter((item) => item.status === PropertyOfferStatus.ACCEPTED).length,
|
|
10015
|
+
hint: 'Offers moved forward in the negotiation flow',
|
|
10016
|
+
},
|
|
10017
|
+
{
|
|
10018
|
+
label: 'Cancelled',
|
|
10019
|
+
value: offers.filter((item) => item.status === PropertyOfferStatus.CANCELLED).length,
|
|
10020
|
+
hint: 'Closed or withdrawn buyer negotiations',
|
|
10021
|
+
},
|
|
9909
10022
|
];
|
|
9910
10023
|
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
9911
10024
|
ngOnInit() {
|
|
@@ -9954,7 +10067,7 @@ class PropertyMarketOfferIndex extends BaseComponent {
|
|
|
9954
10067
|
});
|
|
9955
10068
|
}
|
|
9956
10069
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9957
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferIndex, isStandalone: true, selector: "rolatech-property-market-offer-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"
|
|
10070
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferIndex, isStandalone: true, selector: "rolatech-property-market-offer-index", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n } } @else if (offers().length > 0) { @for (item of offers(); track item.id) {\n <rolatech-property-offer-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [offer]=\"item\"\n ></rolatech-property-offer-item>\n } } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferItemComponent, selector: "rolatech-property-offer-item", inputs: ["offer"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: OfferItemSkeleton, selector: "rolatech-offer-item-skeleton" }, { kind: "component", type: PropertyWorkspaceShell, selector: "rolatech-property-workspace-shell", inputs: ["eyebrow", "title", "description", "stats"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
9958
10071
|
}
|
|
9959
10072
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferIndex, decorators: [{
|
|
9960
10073
|
type: Component,
|
|
@@ -9966,7 +10079,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
9966
10079
|
MatPaginatorModule,
|
|
9967
10080
|
OfferItemSkeleton,
|
|
9968
10081
|
PropertyWorkspaceShell,
|
|
9969
|
-
], encapsulation: ViewEncapsulation.None, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"
|
|
10082
|
+
], encapsulation: ViewEncapsulation.None, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n\n <rolatech-property-workspace-shell\n eyebrow=\"Buyer workspace\"\n title=\"offers\"\n description=\"Follow every market-side offer you submitted as an agent buyer, keep counter-offers readable, and move into each negotiation without dropping the buyer context.\"\n [stats]=\"stats()\"\n >\n <nav workspace-filters class=\"property-market-offer-index__status-nav\" aria-label=\"Buyer offer status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"property-market-offer-index__status-link\"\n [class.property-market-offer-index__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <section class=\"property-market-offer-index__list\">\n @if (loading) { @for (row of [0, 1, 2, 3]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n } } @else if (offers().length > 0) { @for (item of offers(); track item.id) {\n <rolatech-property-offer-item\n class=\"cursor-pointer\"\n [routerLink]=\"['./', item.id]\"\n [offer]=\"item\"\n ></rolatech-property-offer-item>\n } } @else {\n <div class=\"property-market-offer-index__empty\">\n <h3>No buyer offers yet</h3>\n <p>Your market-side offers and any counter-offers will appear here once you start negotiating as a buyer.</p>\n </div>\n }\n\n <mat-paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n </rolatech-property-workspace-shell>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}.property-market-offer-index__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.property-market-offer-index__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none;transition:border-color .2s ease,background .2s ease,color .2s ease}.property-market-offer-index__status-link:hover,.property-market-offer-index__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.property-market-offer-index__list{display:flex;flex-direction:column;gap:1rem}.property-market-offer-index__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.property-market-offer-index__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.property-market-offer-index__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:768px){.property-market-page{padding:1.25rem}}\n"] }]
|
|
9970
10083
|
}] });
|
|
9971
10084
|
|
|
9972
10085
|
class PropertyMarketOfferDetail {
|
|
@@ -9976,11 +10089,11 @@ class PropertyMarketOfferDetail {
|
|
|
9976
10089
|
{ label: 'Offer detail' },
|
|
9977
10090
|
];
|
|
9978
10091
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9979
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyMarketOfferDetail, isStandalone: true, selector: "rolatech-property-market-offer-detail", ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferDetailComponent, selector: "rolatech-property-offer-detail" }] });
|
|
10092
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: PropertyMarketOfferDetail, isStandalone: true, selector: "rolatech-property-market-offer-detail", ngImport: i0, template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail\n [mode]=\"'agent'\"\n [market]=\"'tenant'\"\n eyebrow=\"Market workflow\"\n title=\"Offer detail\"\n description=\"Track buyer-side negotiations, compare counter-offers, and keep every response in the same market workspace.\"\n backLabel=\"Back to buyer offers\"\n ></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferDetailComponent, selector: "rolatech-property-offer-detail", inputs: ["mode", "market", "eyebrow", "title", "description", "backLabel"] }] });
|
|
9980
10093
|
}
|
|
9981
10094
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferDetail, decorators: [{
|
|
9982
10095
|
type: Component,
|
|
9983
|
-
args: [{ selector: 'rolatech-property-market-offer-detail', imports: [CommonModule, PropertyMarketBreadcrumbComponent, PropertyOfferDetailComponent], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"] }]
|
|
10096
|
+
args: [{ selector: 'rolatech-property-market-offer-detail', imports: [CommonModule, PropertyMarketBreadcrumbComponent, PropertyOfferDetailComponent], template: "<div class=\"property-market-page\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs\"></rolatech-property-market-breadcrumb>\n <rolatech-property-offer-detail\n [mode]=\"'agent'\"\n [market]=\"'tenant'\"\n eyebrow=\"Market workflow\"\n title=\"Offer detail\"\n description=\"Track buyer-side negotiations, compare counter-offers, and keep every response in the same market workspace.\"\n backLabel=\"Back to buyer offers\"\n ></rolatech-property-offer-detail>\n</div>\n", styles: [":host{display:block}.property-market-page{display:flex;flex-direction:column;gap:1rem;padding:1rem}\n"] }]
|
|
9984
10097
|
}] });
|
|
9985
10098
|
|
|
9986
10099
|
class PropertyMarketViewingRequest extends BaseComponent {
|
|
@@ -10073,7 +10186,7 @@ class PropertyMarketViewingRequest extends BaseComponent {
|
|
|
10073
10186
|
this.viewing.moveInDate = date ? this.formatDateInput(date) : undefined;
|
|
10074
10187
|
}
|
|
10075
10188
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10076
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketViewingRequest, isStandalone: true, selector: "rolatech-letting-viewing-request", usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three\n viewing options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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" }] });
|
|
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" }] });
|
|
10077
10190
|
}
|
|
10078
10191
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketViewingRequest, decorators: [{
|
|
10079
10192
|
type: Component,
|
|
@@ -10092,7 +10205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
10092
10205
|
PropertyPricingComponent,
|
|
10093
10206
|
PropertyMarketBreadcrumbComponent,
|
|
10094
10207
|
MatSelectModule,
|
|
10095
|
-
], template: "<div class=\"property-market-viewing-request\">\n @if (property(); as property) {\n <section class=\"property-market-viewing-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-viewing-request__hero-copy\">\n <span class=\"property-market-viewing-request__eyebrow\" i18n>Buyer workflow</span>\n <h1 class=\"property-market-viewing-request__title\" i18n>Request a viewing for {{ property.title }}</h1>\n <p class=\"property-market-viewing-request__description\" i18n>\n This request is submitted as an agent enquiry. Share your buyer profile, preferred move-in window, and three\n viewing options so the listing team can confirm the best slot quickly.\n </p>\n </div>\n\n <div class=\"property-market-viewing-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-viewing-request__fact\">\n <span class=\"property-market-viewing-request__fact-value\">\n @if (fact.label === 'Available') {\n {{ fact.value | date: 'mediumDate' }}\n } @else {\n {{ fact.value }}\n }\n </span>\n <span class=\"property-market-viewing-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-viewing-request__content\">\n <main class=\"property-market-viewing-request__main\">\n <section class=\"property-market-viewing-request__panel\">\n <div class=\"property-market-viewing-request__panel-head\">\n <div>\n <span class=\"property-market-viewing-request__panel-eyebrow\" i18n>Viewing brief</span>\n <h2 class=\"property-market-viewing-request__panel-title\" i18n>Tell the listing team about this buyer</h2>\n </div>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>First name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.firstName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.lastName\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"email\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Phone</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.phone\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Applicant type</mat-label>\n <mat-select [(ngModel)]=\"viewing.applicantType\">\n @for (applicantType of applicantTypes | keyvalue; track applicantType.key) {\n <mat-option [value]=\"applicantType.key\">\n {{ applicantType.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Job title</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.jobTitle\" />\n </mat-form-field>\n </div>\n\n <div class=\"property-market-viewing-request__grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Annual income</mat-label>\n <span matTextPrefix>\u00A3 </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"] }]
|
|
10096
10209
|
}], ctorParameters: () => [] });
|
|
10097
10210
|
|
|
10098
10211
|
class PropertyMarketOfferRequest extends BaseComponent {
|
|
@@ -10190,7 +10303,7 @@ class PropertyMarketOfferRequest extends BaseComponent {
|
|
|
10190
10303
|
}
|
|
10191
10304
|
}
|
|
10192
10305
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10193
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferRequest, isStandalone: true, selector: "rolatech-property-market-offer-request", providers: [OfferFormFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n
|
|
10306
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: PropertyMarketOfferRequest, isStandalone: true, selector: "rolatech-property-market-offer-request", providers: [OfferFormFacade], usesInheritance: true, ngImport: i0, template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review, approval, or\n counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs that\n take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i3$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertyMarketBreadcrumbComponent, selector: "rolatech-property-market-breadcrumb", inputs: ["items"] }, { kind: "component", type: PropertyOfferRentalForm, selector: "rolatech-property-offer-rental-form", inputs: ["agentWorkflow"] }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
|
|
10194
10307
|
}
|
|
10195
10308
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: PropertyMarketOfferRequest, decorators: [{
|
|
10196
10309
|
type: Component,
|
|
@@ -10203,7 +10316,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
10203
10316
|
PropertyMarketBreadcrumbComponent,
|
|
10204
10317
|
PropertyOfferRentalForm,
|
|
10205
10318
|
PropertyOfferSaleForm,
|
|
10206
|
-
], providers: [OfferFormFacade], template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n
|
|
10319
|
+
], providers: [OfferFormFacade], template: "<div class=\"property-market-offer-request\">\n @if (property(); as property) {\n <section class=\"property-market-offer-request__hero\">\n <rolatech-property-market-breadcrumb [items]=\"breadcrumbs()\"></rolatech-property-market-breadcrumb>\n\n <div class=\"property-market-offer-request__hero-copy\">\n <span class=\"property-market-offer-request__eyebrow\" i18n>Market workflow</span>\n <h1 class=\"property-market-offer-request__title\">Create {{ offerTypeLabel() }} for {{ property.title }}</h1>\n <p class=\"property-market-offer-request__description\" i18n>\n Share your terms in a buyer-facing flow that matches the property detail experience, then track any review, approval, or\n counter-offer inside your offers inbox.\n </p>\n </div>\n\n <div class=\"property-market-offer-request__fact-grid\">\n @for (fact of heroFacts(); track fact.label) {\n <div class=\"property-market-offer-request__fact\">\n <span class=\"property-market-offer-request__fact-value\">\n @if (fact.label === 'Available') { {{ fact.value | date: 'mediumDate' }} } @else { {{ fact.value }} }\n </span>\n <span class=\"property-market-offer-request__fact-label\">{{ fact.label }}</span>\n </div>\n }\n </div>\n </section>\n\n <div class=\"property-market-offer-request__content\">\n <main class=\"property-market-offer-request__main\">\n <section class=\"property-market-offer-request__panel\">\n <div class=\"property-market-offer-request__panel-head\">\n <div>\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Offer terms</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Present a clean, review-ready offer</h2>\n </div>\n </div>\n\n <div class=\"property-market-offer-request__form\">\n @if (facade.isRental()) {\n <rolatech-property-offer-rental-form [agentWorkflow]=\"true\" />\n } @else {\n <rolatech-property-offer-sale-form />\n }\n </div>\n\n <div class=\"property-market-offer-request__actions\">\n <button\n mat-flat-button\n class=\"property-market-offer-request__submit\"\n [disabled]=\"facade.submitting()\"\n (click)=\"submit()\"\n >\n <span class=\"property-market-offer-request__submit-content\">\n @if (facade.submitting()) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\"></mat-progress-spinner>\n }\n <span>{{ facade.submitting() ? 'Submitting offer...' : 'Submit offer' }}</span>\n </span>\n </button>\n </div>\n\n @if (facade.submitError()) {\n <p class=\"property-market-offer-request__error\">{{ facade.submitError() }}</p>\n }\n </section>\n </main>\n\n <aside class=\"property-market-offer-request__side\">\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <div class=\"property-market-offer-request__summary-head\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>Property snapshot</span>\n <h2 class=\"property-market-offer-request__panel-title\">{{ property.title }}</h2>\n </div>\n\n <rolatech-thumbnail [src]=\"property.media[0]?.url ?? ''\" size=\"small\"></rolatech-thumbnail>\n <rolatech-property-pricing [property]=\"property\"></rolatech-property-pricing>\n </section>\n\n <section class=\"property-market-offer-request__panel property-market-offer-request__panel--summary\">\n <span class=\"property-market-offer-request__panel-eyebrow\" i18n>After submission</span>\n <h2 class=\"property-market-offer-request__panel-title\" i18n>Keep negotiations in one place</h2>\n <p class=\"property-market-offer-request__description\" i18n>\n Admin review, agent confirmation, and counter-offers all flow back into the offer detail page, with breadcrumbs that\n take you straight back to this property when needed.\n </p>\n </section>\n </aside>\n </div>\n } @else {\n <div class=\"property-market-offer-request__loading\" i18n>Loading property details...</div>\n }\n</div>\n", styles: [":host{display:block}mat-form-field{width:100%}.property-market-offer-request{display:flex;flex-direction:column;gap:1.5rem;padding:1rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 16%,transparent),transparent 34%),linear-gradient(180deg,color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%),var(--rt-base-background, #ffffff))}.property-market-offer-request__hero,.property-market-offer-request__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 22px 50px -44px color-mix(in srgb,var(--rt-text-primary) 20%,transparent)}.property-market-offer-request__hero{display:flex;flex-direction:column;gap:1rem;padding:1.25rem;background:linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__eyebrow,.property-market-offer-request__panel-eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.property-market-offer-request__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.08;font-weight:800}.property-market-offer-request__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.property-market-offer-request__fact-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.property-market-offer-request__fact{display:flex;flex-direction:column;gap:.25rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.2rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.property-market-offer-request__fact-value{color:var(--rt-text-primary);font-size:1.1rem;font-weight:700}.property-market-offer-request__fact-label{color:var(--rt-text-secondary);font-size:.84rem}.property-market-offer-request__content,.property-market-offer-request__main,.property-market-offer-request__side{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__panel{padding:1.25rem}.property-market-offer-request__panel--summary{background:linear-gradient(180deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 90%,var(--rt-brand-color) 10%)),var(--rt-base-background, #ffffff)}.property-market-offer-request__panel-head,.property-market-offer-request__summary-head{display:flex;flex-direction:column;gap:.55rem;margin-bottom:1rem}.property-market-offer-request__panel-title{margin:0;color:var(--rt-text-primary);font-size:1.25rem;font-weight:700}.property-market-offer-request__form{display:flex;flex-direction:column;gap:1rem}.property-market-offer-request__actions{margin-top:1rem}.property-market-offer-request__submit{min-height:3rem;border-radius:9999px}.property-market-offer-request__submit-content{display:inline-flex;align-items:center;gap:.65rem}.property-market-offer-request__error{margin:1rem 0 0;color:var(--mat-sys-error, #b91c1c);white-space:pre-line}.property-market-offer-request__loading{padding:2rem 1rem;color:var(--rt-text-secondary)}@media(min-width:768px){.property-market-offer-request{padding:1.25rem}}@media(min-width:1100px){.property-market-offer-request__content{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(19rem,.85fr);align-items:start}}\n"] }]
|
|
10207
10320
|
}], ctorParameters: () => [] });
|
|
10208
10321
|
|
|
10209
10322
|
class PropertyMarketSavedIndex {
|
|
@@ -11542,4 +11655,4 @@ function provideAngularPropertyFeature() {
|
|
|
11542
11655
|
*/
|
|
11543
11656
|
|
|
11544
11657
|
export { PropertyScope as A, PropertyViewingStatus as B, PropertyViewerCategory as C, propertyViewingStatusCode as D, propertyViewingStatusLabel as E, PropertyOfferTimelineStatus as F, PropertyOfferType as G, PropertyOfferStatus as H, EmploymentStatus as I, ResidencyStatus as J, PropertyApplicantType as K, AdverseCreditStatus as L, Market as M, BedroomRange as N, PriceRange as O, PropertyUtil as P, RentFrequency as R, ViewingTime as V, PropertyViewType as a, PropertyManageItemSkeleton as b, PropertyViewingItemComponent as c, propertyManageOffersRoutes as d, propertyRoutes as e, featureManageRoutes as f, propertyManageRoutes as g, propertyManageViewingsRoutes as h, propertyMarketRoutes as i, propertyListingViewingRoutes as j, propertyListingOfferRoutes as k, propertyAgentOverviewRoutes as l, propertyManageOverviewRoutes as m, propertyListingRoutes as n, provideAngularProperty as o, propertyReviewRoutes as p, provideAngularPropertyFeature as q, PropertyActionsComponent as r, PropertyItemComponent as s, tenantManageRoutes as t, PropertyPricingComponent as u, PropertyPriceType as v, PropertyVideoProvider as w, PropertyStatus as x, PropertyType as y, PropertyInventoryStatus as z };
|
|
11545
|
-
//# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-
|
|
11658
|
+
//# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-CKEFhpfX.mjs.map
|