@rolatech/angular-property 20.2.8-beta.3 → 20.2.8-beta.5
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-DiISgmpU.mjs → rolatech-angular-property-property-index.component-BSsr0oLt.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-index.component-DiISgmpU.mjs.map → rolatech-angular-property-property-index.component-BSsr0oLt.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-DbGIrgfl.mjs → rolatech-angular-property-property-manage-viewings-index.component-gp5KsBTB.mjs} +2 -2
- package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-DbGIrgfl.mjs.map → rolatech-angular-property-property-manage-viewings-index.component-gp5KsBTB.mjs.map} +1 -1
- package/fesm2022/{rolatech-angular-property-rolatech-angular-property-DNGWI7Xr.mjs → rolatech-angular-property-rolatech-angular-property-JGdXP_pX.mjs} +449 -571
- package/fesm2022/rolatech-angular-property-rolatech-angular-property-JGdXP_pX.mjs.map +1 -0
- package/fesm2022/rolatech-angular-property.mjs +1 -1
- package/package.json +5 -5
- package/themes/_default.scss +1 -1
- package/fesm2022/rolatech-angular-property-rolatech-angular-property-DNGWI7Xr.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ElementRef, input, booleanAttribute, effect, HostBinding, ViewEncapsulation, Component, viewChild, PLATFORM_ID, output, computed, signal, model,
|
|
2
|
+
import { inject, ElementRef, input, booleanAttribute, effect, HostBinding, ViewEncapsulation, Component, viewChild, PLATFORM_ID, output, computed, signal, model, Injectable, EventEmitter, Pipe, InjectionToken, ViewChild } from '@angular/core';
|
|
3
3
|
import * as i1$3 from '@angular/material/button';
|
|
4
4
|
import { MatButtonModule } from '@angular/material/button';
|
|
5
5
|
import * as i4$1 from '@angular/material/menu';
|
|
@@ -13,7 +13,7 @@ import { RouterLink, RouterModule, ActivatedRoute, Router, RouterLinkActive, Rou
|
|
|
13
13
|
import { ThumbnailComponent, ImagePlaceholderComponent, Skeleton, ToolbarComponent, Loading, ImagePreviewDialogComponent, BaseComponent, AcceptDialogComponent, RejectDialogComponent, RichLabelComponent, AngularComponentsModule, ConfirmationDialogComponent, TabsComponent, TabComponent, EmptyComponent, ListComponent, ContainerComponent, SearchBar, MediaListComponent, MediaListItemComponent, InputComponent, SpinnerComponent } from '@rolatech/angular-components';
|
|
14
14
|
import { PropertyService, FeatureService, PropertyOfferService, InvoiceService, FloorplanService, PropertyHighlightsService, TimeZoneService, PropertyOfferStatus as PropertyOfferStatus$1, PaymentService, 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, Location, 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';
|
|
@@ -47,7 +47,7 @@ import { DomSanitizer } from '@angular/platform-browser';
|
|
|
47
47
|
import * as i4$2 from '@angular/cdk/text-field';
|
|
48
48
|
import { TextFieldModule } from '@angular/cdk/text-field';
|
|
49
49
|
import * as i5$1 from '@angular/material/progress-spinner';
|
|
50
|
-
import {
|
|
50
|
+
import { MatProgressSpinnerModule, MatProgressSpinner } from '@angular/material/progress-spinner';
|
|
51
51
|
import { MatStepperModule } from '@angular/material/stepper';
|
|
52
52
|
import * as i5 from '@angular/material/slide-toggle';
|
|
53
53
|
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
@@ -1040,11 +1040,11 @@ class PropertyOfferItemComponent {
|
|
|
1040
1040
|
return this.offer().amount.toFixed(2);
|
|
1041
1041
|
}, ...(ngDevMode ? [{ debugName: "total" }] : []));
|
|
1042
1042
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyOfferItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1043
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyOfferItemComponent, isStandalone: true, selector: "rolatech-property-offer-item", inputs: { offer: { classPropertyName: "offer", publicName: "offer", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <
|
|
1043
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyOfferItemComponent, isStandalone: true, selector: "rolatech-property-offer-item", inputs: { offer: { classPropertyName: "offer", publicName: "offer", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <div class=\"flex items-center\">\n <span class=\"\">Offer: {{ offer().id }}</span>\n <!-- Type badge -->\n <span class=\"ml-3 px-2.5 py-1 border border-bg-[--rt-outline] rounded-full text-xs font-semibold\">\n {{ offer().type }}\n </span>\n </div>\n <span class=\"font-medium text-sm text-[--rt-brand-color]\">{{ status[offer().status] }}</span>\n </div>\n @if (offer().item) {\n <div class=\"flex flex-row py-2\">\n @if (offer().item.media.length > 0) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"offer().item.media ? offer().item.media[0].url : ''\" size=\"medium\" mode=\"clip\">\n </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div class=\"font-bold\">{{ offer().item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ offer().item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer().item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer().item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ offer().item.amount | price }}</div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
1044
1044
|
}
|
|
1045
1045
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyOfferItemComponent, decorators: [{
|
|
1046
1046
|
type: Component,
|
|
1047
|
-
args: [{ selector: 'rolatech-property-offer-item', imports: [MatIconModule, ThumbnailComponent, ImagePlaceholderComponent, PricePipe], template: "<div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <
|
|
1047
|
+
args: [{ selector: 'rolatech-property-offer-item', imports: [MatIconModule, ThumbnailComponent, ImagePlaceholderComponent, PricePipe], template: "<div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <div class=\"flex items-center\">\n <span class=\"\">Offer: {{ offer().id }}</span>\n <!-- Type badge -->\n <span class=\"ml-3 px-2.5 py-1 border border-bg-[--rt-outline] rounded-full text-xs font-semibold\">\n {{ offer().type }}\n </span>\n </div>\n <span class=\"font-medium text-sm text-[--rt-brand-color]\">{{ status[offer().status] }}</span>\n </div>\n @if (offer().item) {\n <div class=\"flex flex-row py-2\">\n @if (offer().item.media.length > 0) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"offer().item.media ? offer().item.media[0].url : ''\" size=\"medium\" mode=\"clip\">\n </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div class=\"font-bold\">{{ offer().item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ offer().item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer().item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer().item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ offer().item.amount | price }}</div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n</div>\n" }]
|
|
1048
1048
|
}], propDecorators: { offer: [{ type: i0.Input, args: [{ isSignal: true, alias: "offer", required: true }] }] } });
|
|
1049
1049
|
|
|
1050
1050
|
class OfferItemSkeleton {
|
|
@@ -1066,9 +1066,9 @@ class PropertyManageOfferIndexComponent extends BaseComponent {
|
|
|
1066
1066
|
icon: 'dashboard',
|
|
1067
1067
|
},
|
|
1068
1068
|
{
|
|
1069
|
-
name: '
|
|
1069
|
+
name: 'Submitted',
|
|
1070
1070
|
icon: 'category',
|
|
1071
|
-
status: '
|
|
1071
|
+
status: 'submitted',
|
|
1072
1072
|
},
|
|
1073
1073
|
{
|
|
1074
1074
|
name: 'Accepted',
|
|
@@ -1158,7 +1158,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
1158
1158
|
], template: "<rolatech-toolbar title=\"Offers\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [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 <rolatech-list>\n @if (offers() && offers().length > 0) {\n @for (item of offers(); track item) {\n <rolatech-property-offer-item [routerLink]=\"['./', item.id]\" [offer]=\"item\"></rolatech-property-offer-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\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" }]
|
|
1159
1159
|
}] });
|
|
1160
1160
|
|
|
1161
|
-
const MY_FORMATS$
|
|
1161
|
+
const MY_FORMATS$9 = {
|
|
1162
1162
|
parse: {
|
|
1163
1163
|
dateInput: 'YYYY-MM-DD',
|
|
1164
1164
|
},
|
|
@@ -1183,7 +1183,7 @@ class OfferEdit extends BaseComponent {
|
|
|
1183
1183
|
useClass: MomentDateAdapter,
|
|
1184
1184
|
deps: [MAT_DATE_LOCALE],
|
|
1185
1185
|
},
|
|
1186
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1186
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$9 },
|
|
1187
1187
|
], 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: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatOptionModule }] });
|
|
1188
1188
|
}
|
|
1189
1189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferEdit, decorators: [{
|
|
@@ -1194,16 +1194,301 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
1194
1194
|
useClass: MomentDateAdapter,
|
|
1195
1195
|
deps: [MAT_DATE_LOCALE],
|
|
1196
1196
|
},
|
|
1197
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1197
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$9 },
|
|
1198
1198
|
], 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"] }]
|
|
1199
1199
|
}], propDecorators: { offer: [{ type: i0.Input, args: [{ isSignal: true, alias: "offer", required: true }] }, { type: i0.Output, args: ["offerChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
|
|
1200
1200
|
|
|
1201
|
+
class OfferDetailContext {
|
|
1202
|
+
api = inject(PropertyOfferService);
|
|
1203
|
+
// input
|
|
1204
|
+
offerId = signal(null, ...(ngDevMode ? [{ debugName: "offerId" }] : []));
|
|
1205
|
+
// state (single source of truth)
|
|
1206
|
+
offer = signal(null, ...(ngDevMode ? [{ debugName: "offer" }] : []));
|
|
1207
|
+
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1208
|
+
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
1209
|
+
constructor() {
|
|
1210
|
+
effect(() => {
|
|
1211
|
+
const id = this.offerId();
|
|
1212
|
+
if (!id)
|
|
1213
|
+
return;
|
|
1214
|
+
this.fetchOffer(id);
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
fetchOffer(id) {
|
|
1218
|
+
this.loading.set(true);
|
|
1219
|
+
this.error.set(null);
|
|
1220
|
+
this.api.getOffer(id).subscribe({
|
|
1221
|
+
next: (res) => {
|
|
1222
|
+
// adapt if your API shape is { data: ... }
|
|
1223
|
+
this.offer.set(res.data ?? res);
|
|
1224
|
+
},
|
|
1225
|
+
error: (err) => {
|
|
1226
|
+
this.error.set('Failed to load offer');
|
|
1227
|
+
console.error(err);
|
|
1228
|
+
},
|
|
1229
|
+
complete: () => {
|
|
1230
|
+
this.loading.set(false);
|
|
1231
|
+
},
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
init(id) {
|
|
1235
|
+
this.offerId.set(id);
|
|
1236
|
+
}
|
|
1237
|
+
isRental = computed(() => this.offer()?.type === 'RENTAL', ...(ngDevMode ? [{ debugName: "isRental" }] : []));
|
|
1238
|
+
isSale = computed(() => this.offer()?.type === 'SALE', ...(ngDevMode ? [{ debugName: "isSale" }] : []));
|
|
1239
|
+
invoiceOptionLabel = {
|
|
1240
|
+
COMBINED: 'Combined',
|
|
1241
|
+
SEPARATE: 'Separate',
|
|
1242
|
+
};
|
|
1243
|
+
asText(v) {
|
|
1244
|
+
if (v === null || v === undefined)
|
|
1245
|
+
return '—';
|
|
1246
|
+
if (typeof v === 'string' && v.trim() === '')
|
|
1247
|
+
return '—';
|
|
1248
|
+
return String(v);
|
|
1249
|
+
}
|
|
1250
|
+
asBool(v) {
|
|
1251
|
+
if (v === null || v === undefined)
|
|
1252
|
+
return '—';
|
|
1253
|
+
return v ? 'Yes' : 'No';
|
|
1254
|
+
}
|
|
1255
|
+
asMoney(amount) {
|
|
1256
|
+
if (amount === null || amount === undefined)
|
|
1257
|
+
return '—';
|
|
1258
|
+
try {
|
|
1259
|
+
return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(amount);
|
|
1260
|
+
}
|
|
1261
|
+
catch {
|
|
1262
|
+
return String(amount);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
asList(list) {
|
|
1266
|
+
if (!list || list.length === 0)
|
|
1267
|
+
return '—';
|
|
1268
|
+
return list.join(', ');
|
|
1269
|
+
}
|
|
1270
|
+
ageFromDob(dob) {
|
|
1271
|
+
if (!dob)
|
|
1272
|
+
return '—';
|
|
1273
|
+
const birth = new Date(dob);
|
|
1274
|
+
if (Number.isNaN(birth.getTime()))
|
|
1275
|
+
return '—';
|
|
1276
|
+
const today = new Date();
|
|
1277
|
+
let age = today.getFullYear() - birth.getFullYear();
|
|
1278
|
+
const m = today.getMonth() - birth.getMonth();
|
|
1279
|
+
if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) {
|
|
1280
|
+
age--;
|
|
1281
|
+
}
|
|
1282
|
+
return age >= 0 ? `${age}` : '—';
|
|
1283
|
+
}
|
|
1284
|
+
fullAddress(a) {
|
|
1285
|
+
if (!a)
|
|
1286
|
+
return '—';
|
|
1287
|
+
const parts = [a.line1, a.line2, a.city, a.postcode, a.country].filter((p) => !!p && p.trim() !== '');
|
|
1288
|
+
return parts.length ? parts.join(', ') : '—';
|
|
1289
|
+
}
|
|
1290
|
+
isEmptyAddress(a) {
|
|
1291
|
+
if (!a)
|
|
1292
|
+
return true;
|
|
1293
|
+
const parts = [a.line1, a.line2, a.city, a.postcode, a.country].map((v) => (v ?? '').trim());
|
|
1294
|
+
return parts.every((v) => v === '');
|
|
1295
|
+
}
|
|
1296
|
+
isEmptyGuarantor(g) {
|
|
1297
|
+
if (!g)
|
|
1298
|
+
return true;
|
|
1299
|
+
// treat "guarantor": { "address": {} } as empty
|
|
1300
|
+
const addressEmpty = this.isEmptyAddress(g.address);
|
|
1301
|
+
const fields = [
|
|
1302
|
+
g.fullName,
|
|
1303
|
+
g.dob,
|
|
1304
|
+
g.relationship,
|
|
1305
|
+
g.phone,
|
|
1306
|
+
g.email,
|
|
1307
|
+
g.type,
|
|
1308
|
+
g.employmentStatus,
|
|
1309
|
+
g.companyName,
|
|
1310
|
+
g.jobTitle,
|
|
1311
|
+
g.annualSalary,
|
|
1312
|
+
g.companyNumber,
|
|
1313
|
+
g.taxReturnSubmitted,
|
|
1314
|
+
g.additionalFinancialSupport,
|
|
1315
|
+
];
|
|
1316
|
+
const fieldsEmpty = fields.every((v) => v === null || v === undefined || (typeof v === 'string' && v.trim() === ''));
|
|
1317
|
+
return addressEmpty && fieldsEmpty;
|
|
1318
|
+
}
|
|
1319
|
+
isIndividual(t) {
|
|
1320
|
+
return t.applicantType === 'INDIVIDUAL';
|
|
1321
|
+
}
|
|
1322
|
+
isCorporate(t) {
|
|
1323
|
+
return t.applicantType === 'CORPORATE';
|
|
1324
|
+
}
|
|
1325
|
+
isStudent(t) {
|
|
1326
|
+
return t.applicantType === 'STUDENT';
|
|
1327
|
+
}
|
|
1328
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferDetailContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1329
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferDetailContext });
|
|
1330
|
+
}
|
|
1331
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferDetailContext, decorators: [{
|
|
1332
|
+
type: Injectable
|
|
1333
|
+
}], ctorParameters: () => [] });
|
|
1334
|
+
|
|
1335
|
+
class OfferRentalTermsCard {
|
|
1336
|
+
ctx = inject(OfferDetailContext);
|
|
1337
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferRentalTermsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1338
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.rentalTerms</div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : (rt.smoker ? 'Yes' : 'No') }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furnitureRequirement ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
1339
|
+
}
|
|
1340
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferRentalTermsCard, decorators: [{
|
|
1341
|
+
type: Component,
|
|
1342
|
+
args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.rentalTerms</div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : (rt.smoker ? 'Yes' : 'No') }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furnitureRequirement ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n" }]
|
|
1343
|
+
}] });
|
|
1344
|
+
|
|
1345
|
+
class OfferTenantExpanded {
|
|
1346
|
+
ctx = inject(OfferDetailContext);
|
|
1347
|
+
tenant = input.required(...(ngDevMode ? [{ debugName: "tenant" }] : []));
|
|
1348
|
+
formatAddress(a) {
|
|
1349
|
+
const parts = [a?.line1, a?.line2, a?.city, a?.postcode, a?.country].filter((v) => !!v && v.trim() !== '');
|
|
1350
|
+
return parts.length ? parts.join(', ') : '—';
|
|
1351
|
+
}
|
|
1352
|
+
genderLabel = {
|
|
1353
|
+
MALE: 'Male',
|
|
1354
|
+
FEMALE: 'Female',
|
|
1355
|
+
OTHER: 'Other',
|
|
1356
|
+
PREFER_NOT_TO_SAY: 'Prefer not to say',
|
|
1357
|
+
};
|
|
1358
|
+
guarantor = computed(() => this.tenant()?.guarantor ?? null, ...(ngDevMode ? [{ debugName: "guarantor" }] : []));
|
|
1359
|
+
hasGuarantor = computed(() => !!this.guarantor(), ...(ngDevMode ? [{ debugName: "hasGuarantor" }] : []));
|
|
1360
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantExpanded, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1361
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferTenantExpanded, isStandalone: true, selector: "rolatech-offer-tenant-expanded", inputs: { tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"p-4 border-t border-[--rt-outline] space-y-4\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Age</div>\n <div class=\"font-medium\">{{ ctx.ageFromDob(tenant().dob) }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Gender</div>\n <div class=\"font-medium\">@if (tenant().gender) { {{ genderLabel[tenant().gender!] }} } @else { \u2014 }</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Applicant type</div>\n <div class=\"font-medium\">{{ tenant().applicantType ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Nationality</div>\n <div class=\"font-medium\">{{ tenant().nationality }}</div>\n </div>\n\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n <div class=\"font-medium\">{{ formatAddress(tenant().address) }}</div>\n </div>\n </div>\n\n @switch (tenant().applicantType) { @case ('INDIVIDUAL') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Individual</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment</div>\n <div class=\"font-medium\">{{ tenant().employmentStatus ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().companyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ tenant().jobTitle ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('CORPORATE') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Corporate</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company number</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyNumber ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('STUDENT') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Student</div>\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">University</div>\n <div class=\"font-medium\">{{ tenant().universityName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Course</div>\n <div class=\"font-medium\">{{ tenant().courseTitle ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Years of study</div>\n <div class=\"font-medium\">{{ tenant().yearOfStudy ?? '-' }}</div>\n </div>\n </div>\n </div>\n } }\n <!-- \u2705 Guarantor -->\n @if (hasGuarantor(); as _unused) { @if (guarantor(); as g) {\n <div class=\"mt-6 rounded-2xl border border-[--rt-outline] p-4\">\n <div class=\"flex items-center justify-between\">\n <div class=\"text-sm font-semibold\">Guarantor</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">{{ g.type }}</div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-12 gap-3 text-sm\">\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Full name</div>\n <div class=\"font-medium\">{{ g.fullName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Age</div>\n <div class=\"font-medium\">{{ ctx.ageFromDob(g.dob) }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ g.phone ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ g.email ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n @if (g.address; as a) {\n <div class=\"font-medium\">\n {{ a.line1 ?? '\u2014' }} @if (a.line2) { <span>, {{ a.line2 }}</span> } @if (a.city) { <span>, {{ a.city }}</span> } @if\n (a.postcode) { <span>, {{ a.postcode }}</span> } @if (a.country) { <span>, {{ a.country }}</span> }\n </div>\n } @else {\n <div class=\"font-medium\">\u2014</div>\n }\n </div>\n\n <!-- INDIVIDUAL fields -->\n @if (g.type === 'INDIVIDUAL') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment status</div>\n <div class=\"font-medium\">{{ g.employmentStatus ?? '\u2014' }}</div>\n </div>\n\n @if (g.employmentStatus === 'EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ g.jobTitle ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary| price }}</div>\n </div>\n } @if (g.employmentStatus === 'SELF_EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary| price }}</div>\n </div>\n } @if (g.employmentStatus === 'UNEMPLOYED') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional financial support</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ g.additionalFinancialSupport ?? '\u2014' }}</div>\n </div>\n } }\n\n <!-- COMPANY fields (if you later add them on backend) -->\n @if (g.type === 'COMPANY') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company guarantor</div>\n <div class=\"font-medium\">\u2014</div>\n </div>\n }\n </div>\n </div>\n } }\n</div>\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }] });
|
|
1362
|
+
}
|
|
1363
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantExpanded, decorators: [{
|
|
1364
|
+
type: Component,
|
|
1365
|
+
args: [{ selector: 'rolatech-offer-tenant-expanded', imports: [PricePipe], template: "<div class=\"p-4 border-t border-[--rt-outline] space-y-4\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Age</div>\n <div class=\"font-medium\">{{ ctx.ageFromDob(tenant().dob) }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Gender</div>\n <div class=\"font-medium\">@if (tenant().gender) { {{ genderLabel[tenant().gender!] }} } @else { \u2014 }</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Applicant type</div>\n <div class=\"font-medium\">{{ tenant().applicantType ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Nationality</div>\n <div class=\"font-medium\">{{ tenant().nationality }}</div>\n </div>\n\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n <div class=\"font-medium\">{{ formatAddress(tenant().address) }}</div>\n </div>\n </div>\n\n @switch (tenant().applicantType) { @case ('INDIVIDUAL') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Individual</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment</div>\n <div class=\"font-medium\">{{ tenant().employmentStatus ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().companyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ tenant().jobTitle ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('CORPORATE') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Corporate</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company number</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyNumber ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('STUDENT') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Student</div>\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">University</div>\n <div class=\"font-medium\">{{ tenant().universityName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Course</div>\n <div class=\"font-medium\">{{ tenant().courseTitle ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Years of study</div>\n <div class=\"font-medium\">{{ tenant().yearOfStudy ?? '-' }}</div>\n </div>\n </div>\n </div>\n } }\n <!-- \u2705 Guarantor -->\n @if (hasGuarantor(); as _unused) { @if (guarantor(); as g) {\n <div class=\"mt-6 rounded-2xl border border-[--rt-outline] p-4\">\n <div class=\"flex items-center justify-between\">\n <div class=\"text-sm font-semibold\">Guarantor</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">{{ g.type }}</div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-12 gap-3 text-sm\">\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Full name</div>\n <div class=\"font-medium\">{{ g.fullName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Age</div>\n <div class=\"font-medium\">{{ ctx.ageFromDob(g.dob) }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ g.phone ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ g.email ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n @if (g.address; as a) {\n <div class=\"font-medium\">\n {{ a.line1 ?? '\u2014' }} @if (a.line2) { <span>, {{ a.line2 }}</span> } @if (a.city) { <span>, {{ a.city }}</span> } @if\n (a.postcode) { <span>, {{ a.postcode }}</span> } @if (a.country) { <span>, {{ a.country }}</span> }\n </div>\n } @else {\n <div class=\"font-medium\">\u2014</div>\n }\n </div>\n\n <!-- INDIVIDUAL fields -->\n @if (g.type === 'INDIVIDUAL') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment status</div>\n <div class=\"font-medium\">{{ g.employmentStatus ?? '\u2014' }}</div>\n </div>\n\n @if (g.employmentStatus === 'EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ g.jobTitle ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary| price }}</div>\n </div>\n } @if (g.employmentStatus === 'SELF_EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary| price }}</div>\n </div>\n } @if (g.employmentStatus === 'UNEMPLOYED') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional financial support</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ g.additionalFinancialSupport ?? '\u2014' }}</div>\n </div>\n } }\n\n <!-- COMPANY fields (if you later add them on backend) -->\n @if (g.type === 'COMPANY') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company guarantor</div>\n <div class=\"font-medium\">\u2014</div>\n </div>\n }\n </div>\n </div>\n } }\n</div>\n" }]
|
|
1366
|
+
}], propDecorators: { tenant: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenant", required: true }] }] } });
|
|
1367
|
+
|
|
1368
|
+
class OfferTenantCard {
|
|
1369
|
+
// ---- signal inputs
|
|
1370
|
+
index = input.required(...(ngDevMode ? [{ debugName: "index" }] : []));
|
|
1371
|
+
expanded = input.required(...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
1372
|
+
tenant = input.required(...(ngDevMode ? [{ debugName: "tenant" }] : []));
|
|
1373
|
+
// ---- label maps (for tooltips / pretty labels)
|
|
1374
|
+
paymentFrequencyLabel = {
|
|
1375
|
+
MONTHLY: 'Monthly',
|
|
1376
|
+
QUARTERLY: 'Quarterly',
|
|
1377
|
+
SEMI_ANNUALLY: 'Semi-annually',
|
|
1378
|
+
ANNUALLY: 'Annually',
|
|
1379
|
+
};
|
|
1380
|
+
furnitureRequirementLabel = {
|
|
1381
|
+
FURNISHED: 'Furnished',
|
|
1382
|
+
UNFURNISHED: 'Unfurnished',
|
|
1383
|
+
};
|
|
1384
|
+
applicantTypeLabel = {
|
|
1385
|
+
INDIVIDUAL: 'Individual',
|
|
1386
|
+
CORPORATE: 'Corporate',
|
|
1387
|
+
STUDENT: 'Student',
|
|
1388
|
+
};
|
|
1389
|
+
employmentStatusLabel = {
|
|
1390
|
+
EMPLOYED: 'Employed',
|
|
1391
|
+
SELF_EMPLOYED: 'Self-employed',
|
|
1392
|
+
UNEMPLOYED: 'Unemployed',
|
|
1393
|
+
};
|
|
1394
|
+
visaStatusLabel = {
|
|
1395
|
+
CITIZEN: 'Citizen',
|
|
1396
|
+
VISA_HOLDER: 'Visa Holder',
|
|
1397
|
+
WAITING_FOR_VISA: 'Waiting For Visa',
|
|
1398
|
+
NOT_YET_APPLIED_FOR_VISA: 'Not Yet Applied For Visa',
|
|
1399
|
+
};
|
|
1400
|
+
adverseCreditStatusLabel = {
|
|
1401
|
+
NONE: 'None',
|
|
1402
|
+
ACTIVE_CCJ: 'Active Ccj',
|
|
1403
|
+
SATISFIED_CCJ: 'Satisfied Ccj',
|
|
1404
|
+
DEBT_MANAGEMENT_PLAN: 'Debt Management Plan',
|
|
1405
|
+
LOW_CREDIT_SCORE: 'Low Credit Score',
|
|
1406
|
+
OTHER: 'Other',
|
|
1407
|
+
};
|
|
1408
|
+
// ---- callback input instead of Output
|
|
1409
|
+
onToggleInput = input.required(...(ngDevMode ? [{ debugName: "onToggleInput" }] : []));
|
|
1410
|
+
onToggle() {
|
|
1411
|
+
this.onToggleInput()(this.index());
|
|
1412
|
+
}
|
|
1413
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1414
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferTenantCard, isStandalone: true, selector: "rolatech-offer-tenant-card", inputs: { index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: true, transformFunction: null }, tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null }, onToggleInput: { classPropertyName: "onToggleInput", publicName: "onToggleInput", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-outline] bg-[--rt-base-background] overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-start justify-between gap-3 hover:bg-[--rt-raised-background]\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-sm font-semibold truncate\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"text-xs text-[--rt-text-secondary] truncate\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"flex flex-wrap gap-2 pt-1 text-xs\">\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <span class=\"text-xs px-2 py-1 rounded-full border\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span>\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: OfferTenantExpanded, selector: "rolatech-offer-tenant-expanded", inputs: ["tenant"] }] });
|
|
1415
|
+
}
|
|
1416
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantCard, decorators: [{
|
|
1417
|
+
type: Component,
|
|
1418
|
+
args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], host: { class: 'block' }, template: "<div class=\"rounded-xl border border-[--rt-outline] bg-[--rt-base-background] overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-start justify-between gap-3 hover:bg-[--rt-raised-background]\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-sm font-semibold truncate\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"text-xs text-[--rt-text-secondary] truncate\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"flex flex-wrap gap-2 pt-1 text-xs\">\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <span class=\"text-xs px-2 py-1 rounded-full border\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span>\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n }\n</div>\n" }]
|
|
1419
|
+
}], 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 }] }] } });
|
|
1420
|
+
|
|
1421
|
+
class OfferTenantsAccordion {
|
|
1422
|
+
ctx = inject(OfferDetailContext);
|
|
1423
|
+
// tenants snapshot from ctx
|
|
1424
|
+
tenants = computed(() => this.ctx.offer()?.tenants ?? [], ...(ngDevMode ? [{ debugName: "tenants" }] : []));
|
|
1425
|
+
// index-based expanded state (per your requirement)
|
|
1426
|
+
expandedIndexes = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedIndexes" }] : []));
|
|
1427
|
+
isExpanded(i) {
|
|
1428
|
+
return this.expandedIndexes().has(i);
|
|
1429
|
+
}
|
|
1430
|
+
toggle = (index) => {
|
|
1431
|
+
this.expandedIndexes.update((prev) => {
|
|
1432
|
+
const next = new Set(prev);
|
|
1433
|
+
if (next.has(index))
|
|
1434
|
+
next.delete(index);
|
|
1435
|
+
else
|
|
1436
|
+
next.add(index);
|
|
1437
|
+
return next;
|
|
1438
|
+
});
|
|
1439
|
+
};
|
|
1440
|
+
expandAll(count) {
|
|
1441
|
+
const next = new Set();
|
|
1442
|
+
for (let i = 0; i < count; i++)
|
|
1443
|
+
next.add(i);
|
|
1444
|
+
this.expandedIndexes.set(next);
|
|
1445
|
+
}
|
|
1446
|
+
collapseAll() {
|
|
1447
|
+
this.expandedIndexes.set(new Set());
|
|
1448
|
+
}
|
|
1449
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantsAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1450
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", host: { classAttribute: "block" }, ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Tenants</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.tenants</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border bg-[--rt-base-background]\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-[--rt-text-secondary]\">No tenants</div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "component", type: OfferTenantCard, selector: "rolatech-offer-tenant-card", inputs: ["index", "expanded", "tenant", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
1451
|
+
}
|
|
1452
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantsAccordion, decorators: [{
|
|
1453
|
+
type: Component,
|
|
1454
|
+
args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, host: { class: 'block' }, template: "@if (tenants(); as ts) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Tenants</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.tenants</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border bg-[--rt-base-background]\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-[--rt-text-secondary]\">No tenants</div>\n }\n</section>\n}\n" }]
|
|
1455
|
+
}] });
|
|
1456
|
+
|
|
1457
|
+
class OfferItemCard {
|
|
1458
|
+
ctx = inject(OfferDetailContext);
|
|
1459
|
+
firstMediaUrl(o) {
|
|
1460
|
+
const m = o.item?.media?.[0];
|
|
1461
|
+
return m?.url ?? null;
|
|
1462
|
+
}
|
|
1463
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferItemCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1464
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] overflow-hidden\">\n <div class=\"p-4 flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Property item</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">property snapshot</div>\n </div>\n <span class=\"text-xs px-2 py-1 rounded-full border bg-[--rt-base-background]\"> propertyId: {{ o.item.propertyId }} </span>\n </div>\n\n <div class=\"p-4 pt-0 space-y-4\">\n <div class=\"flex flex-col lg:flex-row gap-4\">\n <img\n class=\"w-full lg:w-72 h-44 rounded-xl object-cover bg-gray-100\"\n [src]=\"firstMediaUrl(o) ?? '/assets/placeholder.jpg'\"\n [alt]=\"o.item.title\"\n />\n\n <div class=\"flex-1 space-y-2\">\n <div class=\"text-base font-semibold\">{{ o.item.title }}</div>\n\n <div class=\"grid grid-cols-2 gap-3 text-sm pt-2\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ o.item.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bedrooms</div>\n <div class=\"font-medium\">{{ o.item.bedrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bathrooms</div>\n <div class=\"font-medium\">{{ o.item.bathrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Receptions</div>\n <div class=\"font-medium\">{{ o.item.receptions }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }] });
|
|
1465
|
+
}
|
|
1466
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferItemCard, decorators: [{
|
|
1467
|
+
type: Component,
|
|
1468
|
+
args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] overflow-hidden\">\n <div class=\"p-4 flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Property item</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">property snapshot</div>\n </div>\n <span class=\"text-xs px-2 py-1 rounded-full border bg-[--rt-base-background]\"> propertyId: {{ o.item.propertyId }} </span>\n </div>\n\n <div class=\"p-4 pt-0 space-y-4\">\n <div class=\"flex flex-col lg:flex-row gap-4\">\n <img\n class=\"w-full lg:w-72 h-44 rounded-xl object-cover bg-gray-100\"\n [src]=\"firstMediaUrl(o) ?? '/assets/placeholder.jpg'\"\n [alt]=\"o.item.title\"\n />\n\n <div class=\"flex-1 space-y-2\">\n <div class=\"text-base font-semibold\">{{ o.item.title }}</div>\n\n <div class=\"grid grid-cols-2 gap-3 text-sm pt-2\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ o.item.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bedrooms</div>\n <div class=\"font-medium\">{{ o.item.bedrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bathrooms</div>\n <div class=\"font-medium\">{{ o.item.bathrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Receptions</div>\n <div class=\"font-medium\">{{ o.item.receptions }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n}\n" }]
|
|
1469
|
+
}] });
|
|
1470
|
+
|
|
1471
|
+
class OfferSaleDetailsCard {
|
|
1472
|
+
ctx = inject(OfferDetailContext);
|
|
1473
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferSaleDetailsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1474
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Sale details</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.sale</div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ s.amount ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment method</div>\n <div class=\"font-medium\">{{ s.paymentMethod ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Buyer name</div>\n <div class=\"font-medium\">{{ s.buyerName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ s.phone ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ s.email ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Proposed exchange date</div>\n <div class=\"font-medium\">{{ s.proposedExchangeDate ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Solicitor company</div>\n <div class=\"font-medium\">{{ s.solicitorCompanyName ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n", styles: [""] });
|
|
1475
|
+
}
|
|
1476
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferSaleDetailsCard, decorators: [{
|
|
1477
|
+
type: Component,
|
|
1478
|
+
args: [{ selector: 'rolatech-offer-sale-details-card', imports: [], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Sale details</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.sale</div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ s.amount ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment method</div>\n <div class=\"font-medium\">{{ s.paymentMethod ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Buyer name</div>\n <div class=\"font-medium\">{{ s.buyerName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ s.phone ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ s.email ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Proposed exchange date</div>\n <div class=\"font-medium\">{{ s.proposedExchangeDate ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Solicitor company</div>\n <div class=\"font-medium\">{{ s.solicitorCompanyName ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n" }]
|
|
1479
|
+
}] });
|
|
1480
|
+
|
|
1201
1481
|
class PropertyManageOfferDetailComponent extends BaseComponent {
|
|
1202
1482
|
propertyService = inject(PropertyService);
|
|
1203
1483
|
propertyOfferService = inject(PropertyOfferService);
|
|
1204
1484
|
authUserService = inject(AuthUserService);
|
|
1205
1485
|
invoiceService = inject(InvoiceService);
|
|
1486
|
+
ctx = inject(OfferDetailContext);
|
|
1206
1487
|
invoiceOptionDraft = signal(null, ...(ngDevMode ? [{ debugName: "invoiceOptionDraft" }] : []));
|
|
1488
|
+
invoiceOptionLabel = {
|
|
1489
|
+
COMBINED: 'Combined',
|
|
1490
|
+
SEPARATE: 'Separate',
|
|
1491
|
+
};
|
|
1207
1492
|
offer;
|
|
1208
1493
|
name = '';
|
|
1209
1494
|
property;
|
|
@@ -1214,6 +1499,7 @@ class PropertyManageOfferDetailComponent extends BaseComponent {
|
|
|
1214
1499
|
isSale = computed(() => this.offer.type.toString() === 'SALE', ...(ngDevMode ? [{ debugName: "isSale" }] : []));
|
|
1215
1500
|
ngOnInit() {
|
|
1216
1501
|
this.getOffer();
|
|
1502
|
+
this.ctx.init(this.id);
|
|
1217
1503
|
}
|
|
1218
1504
|
getOffer() {
|
|
1219
1505
|
this.propertyOfferService.getOffer(this.id).subscribe({
|
|
@@ -1447,21 +1733,24 @@ class PropertyManageOfferDetailComponent extends BaseComponent {
|
|
|
1447
1733
|
});
|
|
1448
1734
|
}
|
|
1449
1735
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyManageOfferDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1450
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyManageOfferDetailComponent, isStandalone: true, selector: "rolatech-property-manage-offer-detail", usesInheritance: true, ngImport: i0, template: "@if (offer) {\n <rolatech-toolbar [title]=\"status[offer.status]\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button (click)=\"accept()\" i18n>Accept</button>\n <button mat-flat-button (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-button (click)=\"reject()\" i18n>Reject</button>\n <button mat-button (click)=\"edit()\" i18n>Edit</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'HOLDING_DEPOSIT_PAID') {\n <button mat-flat-button (click)=\"underOffer()\" i18n>Under offer</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-flat-button (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <!-- Completed -->\n @if (offer.status.toString() === 'MOVE_IN_PAYMENT_PAID') {\n <button mat-flat-button (click)=\"completed()\" i18n>Completed</button>\n }\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <div class=\"grid gap-4 lg:grid-cols-12 p-4\">\n <!-- Main column -->\n <div class=\"lg:col-span-8 space-y-4\">\n <!-- Summary card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Offer summary</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Key details for pricing, move-in, and terms.</div>\n </div>\n </div>\n\n <div class=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Offer amount</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.amount | price }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">pcm</span>\n </div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"mt-1 text-lg font-semibold\">{{ offer.rentalTerms?.moveInDate | date: 'mediumDate' }}</div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy term</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.tenancyLengthMonths }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">months</span>\n </div>\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Number of tenants</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.tenants?.length }} <span class=\"text-sm font-medium text-[--rt-text-secondary]\"></span>\n </div>\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment status</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.tenants }} <span class=\"text-sm font-medium text-[--rt-text-secondary]\"></span>\n </div>\n </div>\n </div>\n </section>\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-[--rt-border-color] bg-[--rt-surface-2] p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid gap-2 sm:grid-cols-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n <!-- Property card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Property</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Offer property snapshot.</div>\n </div>\n </div>\n <div class=\"flex items-center py-2\">\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"offer.item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ offer.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ offer.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ offer.item.amount | price }}</div>\n </div>\n </div>\n </div>\n </div>\n </section>\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <button mat-menu-item (click)=\"edit()\">Edit</button>\n </mat-menu>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
|
|
1736
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyManageOfferDetailComponent, isStandalone: true, selector: "rolatech-property-manage-offer-detail", providers: [OfferDetailContext], usesInheritance: true, ngImport: i0, template: "@if (offer) {\n <rolatech-toolbar [title]=\"status[offer.status]\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button (click)=\"accept()\" i18n>Accept</button>\n <button mat-flat-button (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-button (click)=\"reject()\" i18n>Reject</button>\n <button mat-button (click)=\"edit()\" i18n>Edit</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'HOLDING_DEPOSIT_PAID') {\n <button mat-flat-button (click)=\"underOffer()\" i18n>Under offer</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-flat-button (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <!-- Completed -->\n @if (offer.status.toString() === 'MOVE_IN_PAYMENT_PAID') {\n <button mat-flat-button (click)=\"completed()\" i18n>Completed</button>\n }\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4\">\n <rolatech-offer-item-card />\n <!-- Summary card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Offer summary</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Key details for pricing, move-in, and terms.</div>\n </div>\n </div>\n\n <div class=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Offer amount</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.amount | price }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">pcm</span>\n </div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"mt-1 text-lg font-semibold\">{{ offer.rentalTerms?.moveInDate | date: 'mediumDate' }}</div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy term</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.tenancyLengthMonths }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">months</span>\n </div>\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Number of tenants</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.tenants?.length }} <span class=\"text-sm font-medium text-[--rt-text-secondary]\"></span>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Tenants card -->\n @if (ctx.isRental()) {\n <rolatech-offer-rental-terms-card />\n <rolatech-offer-tenants-accordion />\n }\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-[--rt-border-color] bg-[--rt-surface-2] p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <button mat-menu-item (click)=\"edit()\">Edit</button>\n </mat-menu>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1$3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i4$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
1451
1737
|
}
|
|
1452
1738
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyManageOfferDetailComponent, decorators: [{
|
|
1453
1739
|
type: Component,
|
|
1454
1740
|
args: [{ selector: 'rolatech-property-manage-offer-detail', imports: [
|
|
1455
1741
|
CommonModule,
|
|
1456
1742
|
ToolbarComponent,
|
|
1457
|
-
ThumbnailComponent,
|
|
1458
1743
|
MatButtonModule,
|
|
1459
1744
|
MatIcon,
|
|
1460
1745
|
MatMenuModule,
|
|
1461
1746
|
PricePipe,
|
|
1462
1747
|
MatRadioModule,
|
|
1463
1748
|
FormsModule,
|
|
1464
|
-
], template: "@if (offer) {\n <rolatech-toolbar [title]=\"status[offer.status]\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button (click)=\"accept()\" i18n>Accept</button>\n <button mat-flat-button (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-button (click)=\"reject()\" i18n>Reject</button>\n <button mat-button (click)=\"edit()\" i18n>Edit</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'HOLDING_DEPOSIT_PAID') {\n <button mat-flat-button (click)=\"underOffer()\" i18n>Under offer</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-flat-button (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <!-- Completed -->\n @if (offer.status.toString() === 'MOVE_IN_PAYMENT_PAID') {\n <button mat-flat-button (click)=\"completed()\" i18n>Completed</button>\n }\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <div class=\"grid gap-4 lg:grid-cols-12 p-4\">\n <!-- Main column -->\n <div class=\"lg:col-span-8 space-y-4\">\n <!-- Summary card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Offer summary</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Key details for pricing, move-in, and terms.</div>\n </div>\n </div>\n\n <div class=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Offer amount</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.amount | price }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">pcm</span>\n </div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"mt-1 text-lg font-semibold\">{{ offer.rentalTerms?.moveInDate | date: 'mediumDate' }}</div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy term</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.tenancyLengthMonths }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">months</span>\n </div>\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Number of tenants</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.tenants?.length }} <span class=\"text-sm font-medium text-[--rt-text-secondary]\"></span>\n </div>\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment status</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.tenants }} <span class=\"text-sm font-medium text-[--rt-text-secondary]\"></span>\n </div>\n </div>\n </div>\n </section>\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-[--rt-border-color] bg-[--rt-surface-2] p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid gap-2 sm:grid-cols-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n <!-- Property card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Property</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Offer property snapshot.</div>\n </div>\n </div>\n <div class=\"flex items-center py-2\">\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"offer.item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ offer.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ offer.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ offer.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ offer.item.amount | price }}</div>\n </div>\n </div>\n </div>\n </div>\n </section>\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <button mat-menu-item (click)=\"edit()\">Edit</button>\n </mat-menu>\n}\n" }]
|
|
1749
|
+
OfferRentalTermsCard,
|
|
1750
|
+
OfferTenantsAccordion,
|
|
1751
|
+
OfferItemCard,
|
|
1752
|
+
OfferSaleDetailsCard,
|
|
1753
|
+
], providers: [OfferDetailContext], template: "@if (offer) {\n <rolatech-toolbar [title]=\"status[offer.status]\" large link=\"../\">\n <div class=\"hidden md:flex gap-2\">\n <button mat-flat-button (click)=\"copyText(true)\">\n <mat-icon>content_copy</mat-icon>\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button (click)=\"accept()\" i18n>Accept</button>\n <button mat-flat-button (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-button (click)=\"reject()\" i18n>Reject</button>\n <button mat-button (click)=\"edit()\" i18n>Edit</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'HOLDING_DEPOSIT_PAID') {\n <button mat-flat-button (click)=\"underOffer()\" i18n>Under offer</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-flat-button (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <!-- Completed -->\n @if (offer.status.toString() === 'MOVE_IN_PAYMENT_PAID') {\n <button mat-flat-button (click)=\"completed()\" i18n>Completed</button>\n }\n </div>\n <div class=\"block md:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </rolatech-toolbar>\n\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4 p-4\">\n <!-- Main column -->\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4\">\n <rolatech-offer-item-card />\n <!-- Summary card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Offer summary</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Key details for pricing, move-in, and terms.</div>\n </div>\n </div>\n\n <div class=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Offer amount</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.amount | price }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">pcm</span>\n </div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"mt-1 text-lg font-semibold\">{{ offer.rentalTerms?.moveInDate | date: 'mediumDate' }}</div>\n </div>\n\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy term</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.rentalTerms?.tenancyLengthMonths }}\n <span class=\"text-sm font-medium text-[--rt-text-secondary]\">months</span>\n </div>\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] p-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Number of tenants</div>\n <div class=\"mt-1 text-lg font-semibold\">\n {{ offer.tenants?.length }} <span class=\"text-sm font-medium text-[--rt-text-secondary]\"></span>\n </div>\n </div>\n </div>\n </section>\n\n <!-- Tenants card -->\n @if (ctx.isRental()) {\n <rolatech-offer-rental-terms-card />\n <rolatech-offer-tenants-accordion />\n }\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n </div>\n\n <!-- Right rail -->\n <div class=\"lg:col-span-4 space-y-4\">\n <!-- Move-in billing -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Move-in billing</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Choose whether Security Deposit and First Rent are combined or separate invoices.\n </div>\n </div>\n\n <button mat-flat-button type=\"button\" (click)=\"saveInvoiceOption(offer.id)\">Save</button>\n </div>\n\n <div class=\"mt-4 rounded-xl border border-[--rt-border-color] bg-[--rt-surface-2] p-3\">\n <mat-radio-group class=\"block\" [value]=\"offer.invoiceOption\" (change)=\"setInvoiceOption($event.value)\">\n <div class=\"grid grid-cols-1 gap-2\">\n <!-- Combined -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"COMBINED\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Combined</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n One invoice with 2 lines: Security deposit + First rent.\n </div>\n </div>\n </div>\n </div>\n\n <!-- Separate -->\n <div\n class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-base-background] p-3 hover:border-[--rt-border-color] transition\"\n >\n <div class=\"flex items-start gap-3\">\n <mat-radio-button value=\"SEPARATE\"></mat-radio-button>\n <div class=\"min-w-0\">\n <div class=\"text-sm font-semibold\">Separate</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">\n Two invoices: Security deposit invoice, then First rent invoice.\n </div>\n </div>\n </div>\n </div>\n </div>\n </mat-radio-group>\n </div>\n </section>\n <!-- Offer user card -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Applicant</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">User ID: {{ offer.id }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n <!-- Offer viewing agent -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Agent</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Contact and identity details.</div>\n </div>\n </div>\n\n <div class=\"mt-4 flex items-center gap-3\">\n <div class=\"h-10 w-10 overflow-hidden rounded-full border bg-[--rt-surface-2]\"></div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold\">{{ name }}</div>\n <div class=\"truncate text-xs text-[--rt-text-secondary]\">Agent ID: {{ offer.agentId }}</div>\n </div>\n </div>\n\n <div class=\"mt-4 space-y-2\">\n @if (offer.id) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">mail</mat-icon>\n <span class=\"truncate\">{{ offer.id }}</span>\n </div>\n }\n @if (agent) {\n <div class=\"flex items-center gap-2 text-sm\">\n <mat-icon class=\"!text-base text-[--rt-text-secondary]\">call</mat-icon>\n <span class=\"truncate\">{{ agent.phone }}</span>\n </div>\n }\n </div>\n\n <div class=\"mt-4 grid grid-cols-2 gap-2\">\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"emailApplicant(offer.id!)\">Email</button>\n <button mat-stroked-button type=\"button\" [disabled]=\"!offer.id\" (click)=\"callApplicant(offer.id!)\">Call</button>\n </div>\n </section>\n\n <!-- Quick actions card (optional) -->\n <section class=\"rounded-2xl border border-[--rt-border-color] bg-[--rt-base-background] p-4\">\n <div class=\"text-sm font-semibold\">Quick actions</div>\n <div class=\"mt-1 text-xs text-[--rt-text-secondary]\">Common operations for this offer.</div>\n <div class=\"mt-4 space-y-2\">\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-flat-button type=\"button\" class=\"w-full\" (click)=\"accept()\">Accept</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"counter()\">Counter</button>\n <button mat-stroked-button type=\"button\" class=\"w-full\" (click)=\"reject()\">Reject</button>\n }\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-flat-button (click)=\"rfPassed()\" class=\"w-full\">References passed</button>\n <button mat-stroked-button (click)=\"rfFailed()\" class=\"w-full\">References failed</button>\n }\n </div>\n </section>\n </div>\n </div>\n\n <mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n <button mat-menu-item (click)=\"copyText(true)\">\n <span i18n>Copy</span>\n </button>\n @if (offer.status.toString() === 'SUBMITTED') {\n <button mat-menu-item (click)=\"accept()\" i18n>Accept</button>\n <button mat-menu-item (click)=\"counter()\" i18n>Counter offer</button>\n <button mat-menu-item (click)=\"reject()\" i18n>Reject</button>\n }\n <!-- References -->\n @if (offer.status.toString() === 'UNDER_OFFER') {\n <button mat-menu-item (click)=\"rfPassed()\" i18n>References passed</button>\n <button mat-menu-item (click)=\"rfFailed()\" i18n>References failed</button>\n }\n <button mat-menu-item (click)=\"edit()\">Edit</button>\n </mat-menu>\n}\n" }]
|
|
1465
1754
|
}] });
|
|
1466
1755
|
|
|
1467
1756
|
const propertyManageOffersRoutes = [
|
|
@@ -1492,7 +1781,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
1492
1781
|
}]
|
|
1493
1782
|
}] });
|
|
1494
1783
|
|
|
1495
|
-
const MY_FORMATS$
|
|
1784
|
+
const MY_FORMATS$8 = {
|
|
1496
1785
|
parse: {
|
|
1497
1786
|
dateInput: 'YYYY-MM-DD',
|
|
1498
1787
|
},
|
|
@@ -1608,7 +1897,7 @@ class PropertyFilterBar {
|
|
|
1608
1897
|
useClass: MomentDateAdapter,
|
|
1609
1898
|
deps: [MAT_DATE_LOCALE],
|
|
1610
1899
|
},
|
|
1611
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1900
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$8 },
|
|
1612
1901
|
], 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: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.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 });
|
|
1613
1902
|
}
|
|
1614
1903
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyFilterBar, decorators: [{
|
|
@@ -1628,7 +1917,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
1628
1917
|
useClass: MomentDateAdapter,
|
|
1629
1918
|
deps: [MAT_DATE_LOCALE],
|
|
1630
1919
|
},
|
|
1631
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
1920
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$8 },
|
|
1632
1921
|
], 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"] }]
|
|
1633
1922
|
}], 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"] }] } });
|
|
1634
1923
|
|
|
@@ -2302,7 +2591,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2302
2591
|
], 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" }]
|
|
2303
2592
|
}], ctorParameters: () => [] });
|
|
2304
2593
|
|
|
2305
|
-
const MY_FORMATS$
|
|
2594
|
+
const MY_FORMATS$7 = {
|
|
2306
2595
|
parse: {
|
|
2307
2596
|
dateInput: 'YYYY-MM-DD',
|
|
2308
2597
|
},
|
|
@@ -2337,7 +2626,7 @@ class PropertyViewingTimeComponent {
|
|
|
2337
2626
|
useClass: MomentDateAdapter,
|
|
2338
2627
|
deps: [MAT_DATE_LOCALE],
|
|
2339
2628
|
},
|
|
2340
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
2629
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
2341
2630
|
], ngImport: i0, template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [(ngModel)]=\"proposedTime()!.date\"\n (dateInput)=\"proposedTime()!.date = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [(ngModel)]=\"proposedTime()!.time\" required readonly>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$1.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.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 }] });
|
|
2342
2631
|
}
|
|
2343
2632
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingTimeComponent, decorators: [{
|
|
@@ -2357,11 +2646,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2357
2646
|
useClass: MomentDateAdapter,
|
|
2358
2647
|
deps: [MAT_DATE_LOCALE],
|
|
2359
2648
|
},
|
|
2360
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$
|
|
2649
|
+
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
2361
2650
|
], template: "<div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing Date</mat-label>\n <!-- [max]=\"maxDate\" -->\n <input\n matInput\n placeholder=\"Viewing date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"viewingDate\"\n [(ngModel)]=\"proposedTime()!.date\"\n (dateInput)=\"proposedTime()!.date = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Viewing time</mat-label>\n <mat-icon matIconPrefix>schedule</mat-icon>\n <mat-select [(ngModel)]=\"proposedTime()!.time\" required readonly>\n @for (d of time; track d) {\n <mat-option [value]=\"d\">\n {{ d }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
|
|
2362
2651
|
}], propDecorators: { output: [{ type: i0.Output, args: ["output"] }], select: [{ type: i0.Output, args: ["select"] }], proposedTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "proposedTime", required: false }] }] } });
|
|
2363
2652
|
|
|
2364
|
-
const MY_FORMATS$
|
|
2653
|
+
const MY_FORMATS$6 = {
|
|
2365
2654
|
parse: {
|
|
2366
2655
|
dateInput: 'YYYY-MM-DD',
|
|
2367
2656
|
},
|
|
@@ -2418,159 +2707,32 @@ class PropertyViewingRequestComponent extends BaseComponent {
|
|
|
2418
2707
|
this.viewing['country'] = event.value;
|
|
2419
2708
|
}
|
|
2420
2709
|
onViewerSelectionChange(event) {
|
|
2421
|
-
this.selectedViewerCategory = event.value;
|
|
2422
|
-
}
|
|
2423
|
-
sendRequest() {
|
|
2424
|
-
this.propertyService.requestViewing(this.id, this.viewing).subscribe({
|
|
2425
|
-
next: (res) => {
|
|
2426
|
-
this.snackBarService.open('Request viewing successfully');
|
|
2427
|
-
this.router.navigate([`/properties/viewings/${res.data.id}`]);
|
|
2428
|
-
},
|
|
2429
|
-
error: (error) => {
|
|
2430
|
-
this.snackBarService.open(error.message);
|
|
2431
|
-
},
|
|
2432
|
-
});
|
|
2433
|
-
}
|
|
2434
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingRequestComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2435
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", providers: [
|
|
2436
|
-
{
|
|
2437
|
-
provide: DateAdapter,
|
|
2438
|
-
useClass: MomentDateAdapter,
|
|
2439
|
-
deps: [MAT_DATE_LOCALE],
|
|
2440
|
-
},
|
|
2441
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
2442
|
-
], usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-2 mb-3 gap-3\">\n <div class=\"w-full md:w-2/3\">\n <div>\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Request a viewing</div>\n <div class=\"mb-3\">\n <mat-radio-group\n aria-label=\"Select an option\"\n [(ngModel)]=\"viewing.viewerCategory\"\n (change)=\"onViewerSelectionChange($event)\"\n >\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 @if (selectedViewerCategory === 'Tenant') {\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 <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 <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 <mat-form-field>\n <mat-label i18n>Tenant type</mat-label>\n <mat-select placeholder=\"Tenant 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 } @else {\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Your email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n </div>\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Number of tenants</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.numberOfTenants\" />\n </mat-form-field>\n </div>\n <mat-form-field>\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 <mat-form-field appearance=\"fill\">\n <input\n matInput\n placeholder=\"Start date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"startDate\"\n [(ngModel)]=\"viewing.startDate\"\n (dateInput)=\"viewing.startDate = $event.value.format('YYYY-MM-DD')\"\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 class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Tenancy length</mat-label>\n <span matTextSuffix>months</span>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.tenancyDuration\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" floatLabel=\"always\">\n <mat-label i18n>Annual income(All tenants combined)</mat-label>\n <span matTextPrefix>\u00A3 </span>\n <input matInput type=\"text\" placeholder=\"00.00\" [(ngModel)]=\"viewing.income\" />\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>Employment Status</mat-label>\n <mat-select placeholder=\"Employment Status\" [(ngModel)]=\"viewing.employmentStatus\">\n @for (item of employmentStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>AdverseCredit Status</mat-label>\n <mat-select placeholder=\"AdverseCredit Status\" [(ngModel)]=\"viewing.adverseCreditStatus\">\n @for (item of adverseCreditStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n <div>\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 @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n </div>\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n <div class=\"w-full md:w-1/2 py-2 mb-3\">\n @if (property) {\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Property info</div>\n <div>\n <rolatech-thumbnail [src]=\"property ? property.media[0].url : ''\" size=\"small\"></rolatech-thumbnail>\n </div>\n <div class=\"text-xl font-bold\">{{ property.title }}</div>\n <div class=\"\">Available: {{ property.availableDate | availableDate }}</div>\n @if (property.market.toString() === 'LETTINGS' && property.rentFrequency.toString() === 'MONTHLY') {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}pcm</div>\n } @else {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}</div>\n }\n }\n </div>\n </div>\n</rolatech-container>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { 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.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: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { 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: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output", "select"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: AvailableDatePipe, name: "availableDate" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
2443
|
-
}
|
|
2444
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingRequestComponent, decorators: [{
|
|
2445
|
-
type: Component,
|
|
2446
|
-
args: [{ selector: 'rolatech-property-viewing-request', imports: [
|
|
2447
|
-
CommonModule,
|
|
2448
|
-
ContainerComponent,
|
|
2449
|
-
MatFormFieldModule,
|
|
2450
|
-
FormsModule,
|
|
2451
|
-
MatInputModule,
|
|
2452
|
-
TextFieldModule,
|
|
2453
|
-
MatButtonModule,
|
|
2454
|
-
ThumbnailComponent,
|
|
2455
|
-
MatSelectModule,
|
|
2456
|
-
MatDatepickerModule,
|
|
2457
|
-
MatRadioModule,
|
|
2458
|
-
PropertyViewingTimeComponent,
|
|
2459
|
-
AvailableDatePipe,
|
|
2460
|
-
PricePipe,
|
|
2461
|
-
], providers: [
|
|
2462
|
-
{
|
|
2463
|
-
provide: DateAdapter,
|
|
2464
|
-
useClass: MomentDateAdapter,
|
|
2465
|
-
deps: [MAT_DATE_LOCALE],
|
|
2466
|
-
},
|
|
2467
|
-
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$7 },
|
|
2468
|
-
], template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-2 mb-3 gap-3\">\n <div class=\"w-full md:w-2/3\">\n <div>\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Request a viewing</div>\n <div class=\"mb-3\">\n <mat-radio-group\n aria-label=\"Select an option\"\n [(ngModel)]=\"viewing.viewerCategory\"\n (change)=\"onViewerSelectionChange($event)\"\n >\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 @if (selectedViewerCategory === 'Tenant') {\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 <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 <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 <mat-form-field>\n <mat-label i18n>Tenant type</mat-label>\n <mat-select placeholder=\"Tenant 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 } @else {\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Your email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n </div>\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Number of tenants</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.numberOfTenants\" />\n </mat-form-field>\n </div>\n <mat-form-field>\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 <mat-form-field appearance=\"fill\">\n <input\n matInput\n placeholder=\"Start date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"startDate\"\n [(ngModel)]=\"viewing.startDate\"\n (dateInput)=\"viewing.startDate = $event.value.format('YYYY-MM-DD')\"\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 class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Tenancy length</mat-label>\n <span matTextSuffix>months</span>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.tenancyDuration\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" floatLabel=\"always\">\n <mat-label i18n>Annual income(All tenants combined)</mat-label>\n <span matTextPrefix>\u00A3 </span>\n <input matInput type=\"text\" placeholder=\"00.00\" [(ngModel)]=\"viewing.income\" />\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>Employment Status</mat-label>\n <mat-select placeholder=\"Employment Status\" [(ngModel)]=\"viewing.employmentStatus\">\n @for (item of employmentStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>AdverseCredit Status</mat-label>\n <mat-select placeholder=\"AdverseCredit Status\" [(ngModel)]=\"viewing.adverseCreditStatus\">\n @for (item of adverseCreditStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n <div>\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 @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n </div>\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n <div class=\"w-full md:w-1/2 py-2 mb-3\">\n @if (property) {\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Property info</div>\n <div>\n <rolatech-thumbnail [src]=\"property ? property.media[0].url : ''\" size=\"small\"></rolatech-thumbnail>\n </div>\n <div class=\"text-xl font-bold\">{{ property.title }}</div>\n <div class=\"\">Available: {{ property.availableDate | availableDate }}</div>\n @if (property.market.toString() === 'LETTINGS' && property.rentFrequency.toString() === 'MONTHLY') {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}pcm</div>\n } @else {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}</div>\n }\n }\n </div>\n </div>\n</rolatech-container>\n", styles: ["mat-form-field{width:100%}\n"] }]
|
|
2469
|
-
}] });
|
|
2470
|
-
|
|
2471
|
-
const MY_FORMATS$6 = {
|
|
2472
|
-
parse: {
|
|
2473
|
-
dateInput: 'YYYY-MM-DD',
|
|
2474
|
-
},
|
|
2475
|
-
display: {
|
|
2476
|
-
dateInput: 'YYYY-MM-DD',
|
|
2477
|
-
monthYearLabel: 'MMM YYYY',
|
|
2478
|
-
dateA11yLabel: 'YYYY-MM-DD',
|
|
2479
|
-
monthYearA11yLabel: 'MMMM YYYY',
|
|
2480
|
-
},
|
|
2481
|
-
};
|
|
2482
|
-
class PropertyOfferComponent extends BaseComponent {
|
|
2483
|
-
authService = inject(AuthService);
|
|
2484
|
-
authUserService = inject(AuthUserService);
|
|
2485
|
-
propertyService = inject(PropertyService);
|
|
2486
|
-
propertyOfferService = inject(PropertyOfferService);
|
|
2487
|
-
offer = {
|
|
2488
|
-
firstName: '',
|
|
2489
|
-
lastName: '',
|
|
2490
|
-
phone: '',
|
|
2491
|
-
email: '',
|
|
2492
|
-
applicantType: '',
|
|
2493
|
-
residencyStatus: '',
|
|
2494
|
-
employmentStatus: '',
|
|
2495
|
-
adverseCreditStatus: '',
|
|
2496
|
-
startDate: '',
|
|
2497
|
-
notes: '',
|
|
2498
|
-
amount: 0,
|
|
2499
|
-
};
|
|
2500
|
-
get formattedAmount() {
|
|
2501
|
-
return this.offer.amount.toFixed(2); // equivalent to | number:'1.2-2'
|
|
2502
|
-
} // applicantTypes = PropertyApplicantType;
|
|
2503
|
-
applicantTypes = Object.keys(PropertyApplicantType); // ['INDIVIDUAL', 'CORPORATE', ...]
|
|
2504
|
-
PropertyApplicantType = PropertyApplicantType;
|
|
2505
|
-
residencyStatus = ResidencyStatus;
|
|
2506
|
-
employmentStatus = EmploymentStatus;
|
|
2507
|
-
adverseCreditStatus = AdverseCreditStatus;
|
|
2508
|
-
selectedCountry;
|
|
2509
|
-
property = signal(null, ...(ngDevMode ? [{ debugName: "property" }] : []));
|
|
2510
|
-
firstImageUrl = computed(() => {
|
|
2511
|
-
const p = this.property();
|
|
2512
|
-
return p?.media?.[0]?.url || '';
|
|
2513
|
-
}, ...(ngDevMode ? [{ debugName: "firstImageUrl" }] : []));
|
|
2514
|
-
minDate = new Date();
|
|
2515
|
-
sending = false;
|
|
2516
|
-
constructor() {
|
|
2517
|
-
super();
|
|
2518
|
-
effect(() => {
|
|
2519
|
-
const p = this.property();
|
|
2520
|
-
if (p) {
|
|
2521
|
-
this.titleService.setTitle(p.title);
|
|
2522
|
-
}
|
|
2523
|
-
});
|
|
2524
|
-
}
|
|
2525
|
-
ngOnInit() {
|
|
2526
|
-
this.getProperty();
|
|
2527
|
-
}
|
|
2528
|
-
onAmountChange(event) {
|
|
2529
|
-
const inputValue = event.target.value;
|
|
2530
|
-
const regex = /^\d*\.?\d{0,2}$/;
|
|
2531
|
-
if (regex.test(inputValue)) {
|
|
2532
|
-
this.offer.amount = inputValue;
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
getProperty() {
|
|
2536
|
-
this.propertyService.get(this.id).subscribe({
|
|
2537
|
-
next: (res) => {
|
|
2538
|
-
this.property.set(res.data);
|
|
2539
|
-
// this.titleService.setTitle(`${this.property.title}`);
|
|
2540
|
-
},
|
|
2541
|
-
});
|
|
2542
|
-
}
|
|
2543
|
-
onCategorySelect(event) {
|
|
2544
|
-
this.offer['category'] = event.value;
|
|
2710
|
+
this.selectedViewerCategory = event.value;
|
|
2545
2711
|
}
|
|
2546
|
-
onResidencyStatusSelect(event) { }
|
|
2547
2712
|
sendRequest() {
|
|
2548
|
-
this.
|
|
2549
|
-
this.propertyOfferService.makeOffer(this.id, this.offer).subscribe({
|
|
2713
|
+
this.propertyService.requestViewing(this.id, this.viewing).subscribe({
|
|
2550
2714
|
next: (res) => {
|
|
2551
|
-
this.
|
|
2552
|
-
this.
|
|
2553
|
-
this.router.navigate([`/properties/offers/${res.data.id}`]);
|
|
2715
|
+
this.snackBarService.open('Request viewing successfully');
|
|
2716
|
+
this.router.navigate([`/properties/viewings/${res.data.id}`]);
|
|
2554
2717
|
},
|
|
2555
2718
|
error: (error) => {
|
|
2556
|
-
this.sending = false;
|
|
2557
2719
|
this.snackBarService.open(error.message);
|
|
2558
2720
|
},
|
|
2559
2721
|
});
|
|
2560
2722
|
}
|
|
2561
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
2562
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type:
|
|
2723
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingRequestComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2724
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyViewingRequestComponent, isStandalone: true, selector: "rolatech-property-viewing-request", providers: [
|
|
2563
2725
|
{
|
|
2564
2726
|
provide: DateAdapter,
|
|
2565
2727
|
useClass: MomentDateAdapter,
|
|
2566
2728
|
deps: [MAT_DATE_LOCALE],
|
|
2567
2729
|
},
|
|
2568
2730
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
|
|
2569
|
-
], usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-2 mb-3 gap-3\">\n <div class=\"w-full md:w-2/3\">\n <div>\n <div class=\"text-xl font-bold py-2 mb-3\">\n <!-- <span i18n>Offer for </span><span>{{ property()?.title : '' }}</span> -->\n <span i18n=\"@@offerFor\">Offer for {{ property()?.title }}</span>\n </div>\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)]=\"offer.firstName\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Last name</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"offer.lastName\" required />\n </mat-form-field>\n </div>\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)]=\"offer.phone\" required />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"offer.email\" required />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" floatLabel=\"always\">\n <mat-label i18n>Offer amount</mat-label>\n <span matTextPrefix>\u00A3 </span>\n <input matInput type=\"text\" placeholder=\"0.00\" [(ngModel)]=\"offer.amount\" required />\n <!-- <input\n matInput\n type=\"number\"\n placeholder=\"0.00\"\n [value]=\"formattedAmount\"\n (input)=\"onAmountChange($event)\"\n required\n /> -->\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Tenancy Length</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"offer.tenancyDuration\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Number Of Tenants</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"offer.numberOfTenants\" required />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>Start date</mat-label>\n <input\n matInput\n placeholder=\"Start date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"startDate\"\n [(ngModel)]=\"offer.startDate\"\n (dateInput)=\"offer.startDate = $event.value.format('YYYY-MM-DD')\"\n readonly\n required\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>\n <mat-label i18n>Applicant type</mat-label>\n <mat-select placeholder=\"Applicant type\" [(ngModel)]=\"offer.applicantType\" required>\n @for (key of applicantTypes; track key) {\n <mat-option [value]=\"key\">\n {{ PropertyApplicantType[key] }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <!-- subscriptSizing=\"dynamic\" -->\n <mat-form-field>\n <mat-label i18n>Residency status</mat-label>\n <mat-select placeholder=\"Residency status\" [(ngModel)]=\"offer.residencyStatus\" required>\n @for (status of residencyStatus | keyvalue; track status) {\n <mat-option [value]=\"status.key\">\n {{ status.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>Employment Status</mat-label>\n <mat-select placeholder=\"Employment Status\" [(ngModel)]=\"offer.employmentStatus\" required>\n @for (item of employmentStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>AdverseCredit Status</mat-label>\n <mat-select placeholder=\"AdverseCredit Status\" [(ngModel)]=\"offer.adverseCreditStatus\" required>\n @for (item of adverseCreditStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div>\n <div class=\"text-md py-2\" i18n>Your message(Optional)</div>\n <mat-form-field appearance=\"fill\">\n <textarea matInput type=\"text\" [(ngModel)]=\"offer.notes\" cdkTextareaAutosize cdkAutosizeMinRows=\"3\"></textarea>\n </mat-form-field>\n </div>\n <!-- <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button> -->\n <button mat-flat-button (click)=\"sendRequest()\" [disabled]=\"sending\">\n <span style=\"display: flex; align-items: center\">\n @if (sending) {\n <mat-progress-spinner diameter=\"20\" mode=\"indeterminate\" [style.marginRight.px]=\"8\"></mat-progress-spinner>\n }\n {{ sending ? 'Requesting...' : 'Send request' }}\n </span>\n </button>\n </div>\n <div class=\"w-full md:w-1/2 py-2 mb-3\">\n @if (property()) {\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Property info</div>\n <div>\n <rolatech-thumbnail [src]=\"firstImageUrl()\" size=\"small\"></rolatech-thumbnail>\n </div>\n <div class=\"text-xl font-bold\">{{ property()!.title }}</div>\n <div class=\"\">Available: {{ property()!.availableDate | availableDate }}</div>\n @if (property()!.market.toString() === 'LETTINGS' && property()!.rentFrequency.toString() === 'MONTHLY') {\n <div class=\"py-3 text-xl font-bold\">{{ property()!.price | price }}pcm</div>\n } @else {\n <div class=\"py-3 text-xl font-bold\">{{ property()!.price | price }}</div>\n }\n }\n </div>\n </div>\n</rolatech-container>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { 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: 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: "directive", type: i4$2.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { 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: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { 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: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: AvailableDatePipe, name: "availableDate" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
2731
|
+
], usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-2 mb-3 gap-3\">\n <div class=\"w-full md:w-2/3\">\n <div>\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Request a viewing</div>\n <div class=\"mb-3\">\n <mat-radio-group\n aria-label=\"Select an option\"\n [(ngModel)]=\"viewing.viewerCategory\"\n (change)=\"onViewerSelectionChange($event)\"\n >\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 @if (selectedViewerCategory === 'Tenant') {\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 <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 <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 <mat-form-field>\n <mat-label i18n>Tenant type</mat-label>\n <mat-select placeholder=\"Tenant 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 } @else {\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Your email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n </div>\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Number of tenants</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.numberOfTenants\" />\n </mat-form-field>\n </div>\n <mat-form-field>\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 <mat-form-field appearance=\"fill\">\n <input\n matInput\n placeholder=\"Start date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"startDate\"\n [(ngModel)]=\"viewing.startDate\"\n (dateInput)=\"viewing.startDate = $event.value.format('YYYY-MM-DD')\"\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 class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Tenancy length</mat-label>\n <span matTextSuffix>months</span>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.tenancyDuration\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" floatLabel=\"always\">\n <mat-label i18n>Annual income(All tenants combined)</mat-label>\n <span matTextPrefix>\u00A3 </span>\n <input matInput type=\"text\" placeholder=\"00.00\" [(ngModel)]=\"viewing.income\" />\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>Employment Status</mat-label>\n <mat-select placeholder=\"Employment Status\" [(ngModel)]=\"viewing.employmentStatus\">\n @for (item of employmentStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>AdverseCredit Status</mat-label>\n <mat-select placeholder=\"AdverseCredit Status\" [(ngModel)]=\"viewing.adverseCreditStatus\">\n @for (item of adverseCreditStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n <div>\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 @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n </div>\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n <div class=\"w-full md:w-1/2 py-2 mb-3\">\n @if (property) {\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Property info</div>\n <div>\n <rolatech-thumbnail [src]=\"property ? property.media[0].url : ''\" size=\"small\"></rolatech-thumbnail>\n </div>\n <div class=\"text-xl font-bold\">{{ property.title }}</div>\n <div class=\"\">Available: {{ property.availableDate | availableDate }}</div>\n @if (property.market.toString() === 'LETTINGS' && property.rentFrequency.toString() === 'MONTHLY') {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}pcm</div>\n } @else {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}</div>\n }\n }\n </div>\n </div>\n</rolatech-container>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { 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.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: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { 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: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i3$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i3$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "component", type: PropertyViewingTimeComponent, selector: "rolatech-property-viewing-time", inputs: ["proposedTime"], outputs: ["output", "select"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: AvailableDatePipe, name: "availableDate" }, { kind: "pipe", type: PricePipe, name: "price" }] });
|
|
2570
2732
|
}
|
|
2571
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
2733
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingRequestComponent, decorators: [{
|
|
2572
2734
|
type: Component,
|
|
2573
|
-
args: [{ selector: 'rolatech-property-
|
|
2735
|
+
args: [{ selector: 'rolatech-property-viewing-request', imports: [
|
|
2574
2736
|
CommonModule,
|
|
2575
2737
|
ContainerComponent,
|
|
2576
2738
|
MatFormFieldModule,
|
|
@@ -2580,9 +2742,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2580
2742
|
MatButtonModule,
|
|
2581
2743
|
ThumbnailComponent,
|
|
2582
2744
|
MatSelectModule,
|
|
2583
|
-
KeyValuePipe,
|
|
2584
2745
|
MatDatepickerModule,
|
|
2585
|
-
|
|
2746
|
+
MatRadioModule,
|
|
2747
|
+
PropertyViewingTimeComponent,
|
|
2586
2748
|
AvailableDatePipe,
|
|
2587
2749
|
PricePipe,
|
|
2588
2750
|
], providers: [
|
|
@@ -2592,8 +2754,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2592
2754
|
deps: [MAT_DATE_LOCALE],
|
|
2593
2755
|
},
|
|
2594
2756
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$6 },
|
|
2595
|
-
], template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-2 mb-3 gap-3\">\n <div class=\"w-full md:w-2/3\">\n <div>\n <div class=\"text-xl font-bold py-2 mb-3\"
|
|
2596
|
-
}]
|
|
2757
|
+
], template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-2 mb-3 gap-3\">\n <div class=\"w-full md:w-2/3\">\n <div>\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Request a viewing</div>\n <div class=\"mb-3\">\n <mat-radio-group\n aria-label=\"Select an option\"\n [(ngModel)]=\"viewing.viewerCategory\"\n (change)=\"onViewerSelectionChange($event)\"\n >\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 @if (selectedViewerCategory === 'Tenant') {\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 <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 <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 <mat-form-field>\n <mat-label i18n>Tenant type</mat-label>\n <mat-select placeholder=\"Tenant 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 } @else {\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Your email</mat-label>\n <input matInput type=\"text\" [(ngModel)]=\"viewing.email\" />\n </mat-form-field>\n </div>\n <div class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Number of tenants</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.numberOfTenants\" />\n </mat-form-field>\n </div>\n <mat-form-field>\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 <mat-form-field appearance=\"fill\">\n <input\n matInput\n placeholder=\"Start date\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"startDate\"\n [(ngModel)]=\"viewing.startDate\"\n (dateInput)=\"viewing.startDate = $event.value.format('YYYY-MM-DD')\"\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 class=\"flex flex-col md:flex-row gap-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label i18n>Tenancy length</mat-label>\n <span matTextSuffix>months</span>\n <input matInput type=\"number\" [(ngModel)]=\"viewing.tenancyDuration\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" floatLabel=\"always\">\n <mat-label i18n>Annual income(All tenants combined)</mat-label>\n <span matTextPrefix>\u00A3 </span>\n <input matInput type=\"text\" placeholder=\"00.00\" [(ngModel)]=\"viewing.income\" />\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>Employment Status</mat-label>\n <mat-select placeholder=\"Employment Status\" [(ngModel)]=\"viewing.employmentStatus\">\n @for (item of employmentStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field>\n <mat-label i18n>AdverseCredit Status</mat-label>\n <mat-select placeholder=\"AdverseCredit Status\" [(ngModel)]=\"viewing.adverseCreditStatus\">\n @for (item of adverseCreditStatus | keyvalue; track item) {\n <mat-option [value]=\"item.key\">\n {{ item.value }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n <div>\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 @for (item of viewing.proposedSlots; track $index) {\n <rolatech-property-viewing-time [proposedTime]=\"item\"></rolatech-property-viewing-time>\n }\n </div>\n </div>\n <button mat-flat-button (click)=\"sendRequest()\" i18n>Send request</button>\n </div>\n <div class=\"w-full md:w-1/2 py-2 mb-3\">\n @if (property) {\n <div class=\"text-xl font-bold py-2 mb-3\" i18n>Property info</div>\n <div>\n <rolatech-thumbnail [src]=\"property ? property.media[0].url : ''\" size=\"small\"></rolatech-thumbnail>\n </div>\n <div class=\"text-xl font-bold\">{{ property.title }}</div>\n <div class=\"\">Available: {{ property.availableDate | availableDate }}</div>\n @if (property.market.toString() === 'LETTINGS' && property.rentFrequency.toString() === 'MONTHLY') {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}pcm</div>\n } @else {\n <div class=\"py-3 text-xl font-bold\">{{ property.price | price }}</div>\n }\n }\n </div>\n </div>\n</rolatech-container>\n", styles: ["mat-form-field{width:100%}\n"] }]
|
|
2758
|
+
}] });
|
|
2597
2759
|
|
|
2598
2760
|
class PropertyOfferIndexComponent extends BaseComponent {
|
|
2599
2761
|
propertyService = inject(PropertyService);
|
|
@@ -2608,9 +2770,9 @@ class PropertyOfferIndexComponent extends BaseComponent {
|
|
|
2608
2770
|
icon: 'dashboard',
|
|
2609
2771
|
},
|
|
2610
2772
|
{
|
|
2611
|
-
name: '
|
|
2773
|
+
name: 'Submitted',
|
|
2612
2774
|
icon: 'category',
|
|
2613
|
-
status: '
|
|
2775
|
+
status: 'submitted',
|
|
2614
2776
|
},
|
|
2615
2777
|
{
|
|
2616
2778
|
name: 'Accepted',
|
|
@@ -2725,252 +2887,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2725
2887
|
}], propDecorators: { viewing: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewing", required: true }] }] } });
|
|
2726
2888
|
|
|
2727
2889
|
class PropertyViewingIndexComponent extends BaseComponent {
|
|
2728
|
-
propertyService = inject(PropertyService);
|
|
2729
|
-
viewerCategory = PropertyViewerCategory;
|
|
2730
|
-
tenantCategory = PropertyApplicantType;
|
|
2731
|
-
viewingStatus = PropertyViewingStatus;
|
|
2732
|
-
filter = false;
|
|
2733
|
-
filterOptions = {
|
|
2734
|
-
type: '',
|
|
2735
|
-
category: '',
|
|
2736
|
-
status: '',
|
|
2737
|
-
};
|
|
2738
|
-
select = 0;
|
|
2739
|
-
links = [
|
|
2740
|
-
{
|
|
2741
|
-
name: 'All',
|
|
2742
|
-
icon: 'dashboard',
|
|
2743
|
-
},
|
|
2744
|
-
{
|
|
2745
|
-
name: 'Completed',
|
|
2746
|
-
icon: 'category',
|
|
2747
|
-
status: 'completed',
|
|
2748
|
-
},
|
|
2749
|
-
{
|
|
2750
|
-
name: 'Cancelled',
|
|
2751
|
-
icon: 'category',
|
|
2752
|
-
status: 'cancelled',
|
|
2753
|
-
},
|
|
2754
|
-
];
|
|
2755
|
-
loading = false;
|
|
2756
|
-
viewings = signal([], ...(ngDevMode ? [{ debugName: "viewings" }] : []));
|
|
2757
|
-
meta;
|
|
2758
|
-
pageEvent;
|
|
2759
|
-
length = 100;
|
|
2760
|
-
pageSize = 15;
|
|
2761
|
-
pageSizeOptions = [5, 10, 25, 100];
|
|
2762
|
-
pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
2763
|
-
ngOnInit() {
|
|
2764
|
-
const sub = this.route.queryParamMap
|
|
2765
|
-
.pipe(map((p) => {
|
|
2766
|
-
const page = p.get('page') ? Number(p.get('page')) : 1;
|
|
2767
|
-
this.pageIndex.set(Math.max(page - 1, 0));
|
|
2768
|
-
const status = p.get('status') || undefined;
|
|
2769
|
-
let filter = '';
|
|
2770
|
-
if (status) {
|
|
2771
|
-
this.select = this.links.findIndex((item) => item.status === status);
|
|
2772
|
-
filter = `status:${status?.toUpperCase()}`;
|
|
2773
|
-
}
|
|
2774
|
-
return {
|
|
2775
|
-
page,
|
|
2776
|
-
filter,
|
|
2777
|
-
limit: p.get('limit') ? Number(p.get('limit')) : 15,
|
|
2778
|
-
sort: p.get('sort') || undefined,
|
|
2779
|
-
};
|
|
2780
|
-
}),
|
|
2781
|
-
// Cheap deep compare via JSON to avoid spam calls when nothing changed
|
|
2782
|
-
map((o) => JSON.stringify(o)), distinctUntilChanged(), map((s) => JSON.parse(s)), switchMap((params) => {
|
|
2783
|
-
this.loading = true;
|
|
2784
|
-
params['sort'] = 'updatedAt desc';
|
|
2785
|
-
return this.propertyService.findViewingsByUser(params).pipe(finalize(() => (this.loading = false)));
|
|
2786
|
-
}))
|
|
2787
|
-
.subscribe({
|
|
2788
|
-
next: (res) => {
|
|
2789
|
-
this.viewings.set(res.data);
|
|
2790
|
-
this.meta = res.meta;
|
|
2791
|
-
this.length = res.meta.pagination.count;
|
|
2792
|
-
this.loading = false;
|
|
2793
|
-
},
|
|
2794
|
-
error: () => {
|
|
2795
|
-
this.viewings.set([]);
|
|
2796
|
-
this.length = 0;
|
|
2797
|
-
this.loading = false;
|
|
2798
|
-
},
|
|
2799
|
-
});
|
|
2800
|
-
// // auto-unsubscribe on destroy
|
|
2801
|
-
this.destroyRef.onDestroy(() => sub.unsubscribe());
|
|
2802
|
-
}
|
|
2803
|
-
statusCompareFn(t1, t2) {
|
|
2804
|
-
return t1 === t2;
|
|
2805
|
-
}
|
|
2806
|
-
onPage(e) {
|
|
2807
|
-
this.router.navigate([], {
|
|
2808
|
-
queryParams: { page: e.pageIndex + 1, limit: e.pageSize },
|
|
2809
|
-
queryParamsHandling: 'merge',
|
|
2810
|
-
replaceUrl: true, // optional: avoid stacking history on every page click
|
|
2811
|
-
});
|
|
2812
|
-
}
|
|
2813
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingIndexComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2814
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyViewingIndexComponent, isStandalone: true, selector: "rolatech-property-viewing-index", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings(); as viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\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</rolatech-container>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { 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: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading", "block"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ListComponent, selector: "rolatech-list" }, { kind: "component", type: EmptyComponent, selector: "rolatech-empty" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { 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"] }] });
|
|
2815
|
-
}
|
|
2816
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingIndexComponent, decorators: [{
|
|
2817
|
-
type: Component,
|
|
2818
|
-
args: [{ selector: 'rolatech-property-viewing-index', imports: [
|
|
2819
|
-
CommonModule,
|
|
2820
|
-
ContainerComponent,
|
|
2821
|
-
RouterModule,
|
|
2822
|
-
TabsComponent,
|
|
2823
|
-
TabComponent,
|
|
2824
|
-
ToolbarComponent,
|
|
2825
|
-
ListComponent,
|
|
2826
|
-
EmptyComponent,
|
|
2827
|
-
MatButtonModule,
|
|
2828
|
-
MatIconModule,
|
|
2829
|
-
FormsModule,
|
|
2830
|
-
MatFormFieldModule,
|
|
2831
|
-
MatDatepickerModule,
|
|
2832
|
-
MatOptionModule,
|
|
2833
|
-
MatInputModule,
|
|
2834
|
-
MatSelectModule,
|
|
2835
|
-
MatButtonModule,
|
|
2836
|
-
PropertyViewingItemComponent,
|
|
2837
|
-
MatPaginatorModule,
|
|
2838
|
-
], template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings(); as viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\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</rolatech-container>\n" }]
|
|
2839
|
-
}] });
|
|
2840
|
-
|
|
2841
|
-
class OfferDetailContext {
|
|
2842
|
-
api = inject(PropertyOfferService);
|
|
2843
|
-
// input
|
|
2844
|
-
offerId = signal(null, ...(ngDevMode ? [{ debugName: "offerId" }] : []));
|
|
2845
|
-
// state (single source of truth)
|
|
2846
|
-
offer = signal(null, ...(ngDevMode ? [{ debugName: "offer" }] : []));
|
|
2847
|
-
loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
2848
|
-
error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
2849
|
-
constructor() {
|
|
2850
|
-
effect(() => {
|
|
2851
|
-
const id = this.offerId();
|
|
2852
|
-
if (!id)
|
|
2853
|
-
return;
|
|
2854
|
-
this.fetchOffer(id);
|
|
2855
|
-
});
|
|
2856
|
-
}
|
|
2857
|
-
fetchOffer(id) {
|
|
2858
|
-
this.loading.set(true);
|
|
2859
|
-
this.error.set(null);
|
|
2860
|
-
this.api.getOffer(id).subscribe({
|
|
2861
|
-
next: (res) => {
|
|
2862
|
-
// adapt if your API shape is { data: ... }
|
|
2863
|
-
this.offer.set(res.data ?? res);
|
|
2864
|
-
},
|
|
2865
|
-
error: (err) => {
|
|
2866
|
-
this.error.set('Failed to load offer');
|
|
2867
|
-
console.error(err);
|
|
2868
|
-
},
|
|
2869
|
-
complete: () => {
|
|
2870
|
-
this.loading.set(false);
|
|
2871
|
-
},
|
|
2872
|
-
});
|
|
2873
|
-
}
|
|
2874
|
-
init(id) {
|
|
2875
|
-
this.offerId.set(id);
|
|
2876
|
-
}
|
|
2877
|
-
isRental = computed(() => this.offer()?.type === 'RENTAL', ...(ngDevMode ? [{ debugName: "isRental" }] : []));
|
|
2878
|
-
isSale = computed(() => this.offer()?.type === 'SALE', ...(ngDevMode ? [{ debugName: "isSale" }] : []));
|
|
2879
|
-
invoiceOptionLabel = {
|
|
2880
|
-
COMBINED: 'Combined',
|
|
2881
|
-
SEPARATE: 'Separate',
|
|
2882
|
-
};
|
|
2883
|
-
asText(v) {
|
|
2884
|
-
if (v === null || v === undefined)
|
|
2885
|
-
return '—';
|
|
2886
|
-
if (typeof v === 'string' && v.trim() === '')
|
|
2887
|
-
return '—';
|
|
2888
|
-
return String(v);
|
|
2889
|
-
}
|
|
2890
|
-
asBool(v) {
|
|
2891
|
-
if (v === null || v === undefined)
|
|
2892
|
-
return '—';
|
|
2893
|
-
return v ? 'Yes' : 'No';
|
|
2894
|
-
}
|
|
2895
|
-
asMoney(amount) {
|
|
2896
|
-
if (amount === null || amount === undefined)
|
|
2897
|
-
return '—';
|
|
2898
|
-
try {
|
|
2899
|
-
return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(amount);
|
|
2900
|
-
}
|
|
2901
|
-
catch {
|
|
2902
|
-
return String(amount);
|
|
2903
|
-
}
|
|
2904
|
-
}
|
|
2905
|
-
asList(list) {
|
|
2906
|
-
if (!list || list.length === 0)
|
|
2907
|
-
return '—';
|
|
2908
|
-
return list.join(', ');
|
|
2909
|
-
}
|
|
2910
|
-
ageFromDob(dob) {
|
|
2911
|
-
if (!dob)
|
|
2912
|
-
return '—';
|
|
2913
|
-
const birth = new Date(dob);
|
|
2914
|
-
if (Number.isNaN(birth.getTime()))
|
|
2915
|
-
return '—';
|
|
2916
|
-
const today = new Date();
|
|
2917
|
-
let age = today.getFullYear() - birth.getFullYear();
|
|
2918
|
-
const m = today.getMonth() - birth.getMonth();
|
|
2919
|
-
if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) {
|
|
2920
|
-
age--;
|
|
2921
|
-
}
|
|
2922
|
-
return age >= 0 ? `${age}` : '—';
|
|
2923
|
-
}
|
|
2924
|
-
fullAddress(a) {
|
|
2925
|
-
if (!a)
|
|
2926
|
-
return '—';
|
|
2927
|
-
const parts = [a.line1, a.line2, a.city, a.postcode, a.country].filter((p) => !!p && p.trim() !== '');
|
|
2928
|
-
return parts.length ? parts.join(', ') : '—';
|
|
2929
|
-
}
|
|
2930
|
-
isEmptyAddress(a) {
|
|
2931
|
-
if (!a)
|
|
2932
|
-
return true;
|
|
2933
|
-
const parts = [a.line1, a.line2, a.city, a.postcode, a.country].map((v) => (v ?? '').trim());
|
|
2934
|
-
return parts.every((v) => v === '');
|
|
2935
|
-
}
|
|
2936
|
-
isEmptyGuarantor(g) {
|
|
2937
|
-
if (!g)
|
|
2938
|
-
return true;
|
|
2939
|
-
// treat "guarantor": { "address": {} } as empty
|
|
2940
|
-
const addressEmpty = this.isEmptyAddress(g.address);
|
|
2941
|
-
const fields = [
|
|
2942
|
-
g.fullName,
|
|
2943
|
-
g.dob,
|
|
2944
|
-
g.relationship,
|
|
2945
|
-
g.phone,
|
|
2946
|
-
g.email,
|
|
2947
|
-
g.type,
|
|
2948
|
-
g.employmentStatus,
|
|
2949
|
-
g.companyName,
|
|
2950
|
-
g.jobTitle,
|
|
2951
|
-
g.annualSalary,
|
|
2952
|
-
g.companyNumber,
|
|
2953
|
-
g.taxReturnSubmitted,
|
|
2954
|
-
g.additionalFinancialSupport,
|
|
2955
|
-
];
|
|
2956
|
-
const fieldsEmpty = fields.every((v) => v === null || v === undefined || (typeof v === 'string' && v.trim() === ''));
|
|
2957
|
-
return addressEmpty && fieldsEmpty;
|
|
2958
|
-
}
|
|
2959
|
-
isIndividual(t) {
|
|
2960
|
-
return t.applicantType === 'INDIVIDUAL';
|
|
2890
|
+
propertyService = inject(PropertyService);
|
|
2891
|
+
viewerCategory = PropertyViewerCategory;
|
|
2892
|
+
tenantCategory = PropertyApplicantType;
|
|
2893
|
+
viewingStatus = PropertyViewingStatus;
|
|
2894
|
+
filter = false;
|
|
2895
|
+
filterOptions = {
|
|
2896
|
+
type: '',
|
|
2897
|
+
category: '',
|
|
2898
|
+
status: '',
|
|
2899
|
+
};
|
|
2900
|
+
select = 0;
|
|
2901
|
+
links = [
|
|
2902
|
+
{
|
|
2903
|
+
name: 'All',
|
|
2904
|
+
icon: 'dashboard',
|
|
2905
|
+
},
|
|
2906
|
+
{
|
|
2907
|
+
name: 'Completed',
|
|
2908
|
+
icon: 'category',
|
|
2909
|
+
status: 'completed',
|
|
2910
|
+
},
|
|
2911
|
+
{
|
|
2912
|
+
name: 'Cancelled',
|
|
2913
|
+
icon: 'category',
|
|
2914
|
+
status: 'cancelled',
|
|
2915
|
+
},
|
|
2916
|
+
];
|
|
2917
|
+
loading = false;
|
|
2918
|
+
viewings = signal([], ...(ngDevMode ? [{ debugName: "viewings" }] : []));
|
|
2919
|
+
meta;
|
|
2920
|
+
pageEvent;
|
|
2921
|
+
length = 100;
|
|
2922
|
+
pageSize = 15;
|
|
2923
|
+
pageSizeOptions = [5, 10, 25, 100];
|
|
2924
|
+
pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
2925
|
+
ngOnInit() {
|
|
2926
|
+
const sub = this.route.queryParamMap
|
|
2927
|
+
.pipe(map((p) => {
|
|
2928
|
+
const page = p.get('page') ? Number(p.get('page')) : 1;
|
|
2929
|
+
this.pageIndex.set(Math.max(page - 1, 0));
|
|
2930
|
+
const status = p.get('status') || undefined;
|
|
2931
|
+
let filter = '';
|
|
2932
|
+
if (status) {
|
|
2933
|
+
this.select = this.links.findIndex((item) => item.status === status);
|
|
2934
|
+
filter = `status:${status?.toUpperCase()}`;
|
|
2935
|
+
}
|
|
2936
|
+
return {
|
|
2937
|
+
page,
|
|
2938
|
+
filter,
|
|
2939
|
+
limit: p.get('limit') ? Number(p.get('limit')) : 15,
|
|
2940
|
+
sort: p.get('sort') || undefined,
|
|
2941
|
+
};
|
|
2942
|
+
}),
|
|
2943
|
+
// Cheap deep compare via JSON to avoid spam calls when nothing changed
|
|
2944
|
+
map((o) => JSON.stringify(o)), distinctUntilChanged(), map((s) => JSON.parse(s)), switchMap((params) => {
|
|
2945
|
+
this.loading = true;
|
|
2946
|
+
params['sort'] = 'updatedAt desc';
|
|
2947
|
+
return this.propertyService.findViewingsByUser(params).pipe(finalize(() => (this.loading = false)));
|
|
2948
|
+
}))
|
|
2949
|
+
.subscribe({
|
|
2950
|
+
next: (res) => {
|
|
2951
|
+
this.viewings.set(res.data);
|
|
2952
|
+
this.meta = res.meta;
|
|
2953
|
+
this.length = res.meta.pagination.count;
|
|
2954
|
+
this.loading = false;
|
|
2955
|
+
},
|
|
2956
|
+
error: () => {
|
|
2957
|
+
this.viewings.set([]);
|
|
2958
|
+
this.length = 0;
|
|
2959
|
+
this.loading = false;
|
|
2960
|
+
},
|
|
2961
|
+
});
|
|
2962
|
+
// // auto-unsubscribe on destroy
|
|
2963
|
+
this.destroyRef.onDestroy(() => sub.unsubscribe());
|
|
2961
2964
|
}
|
|
2962
|
-
|
|
2963
|
-
return
|
|
2965
|
+
statusCompareFn(t1, t2) {
|
|
2966
|
+
return t1 === t2;
|
|
2964
2967
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2968
|
+
onPage(e) {
|
|
2969
|
+
this.router.navigate([], {
|
|
2970
|
+
queryParams: { page: e.pageIndex + 1, limit: e.pageSize },
|
|
2971
|
+
queryParamsHandling: 'merge',
|
|
2972
|
+
replaceUrl: true, // optional: avoid stacking history on every page click
|
|
2973
|
+
});
|
|
2967
2974
|
}
|
|
2968
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
2969
|
-
static
|
|
2975
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingIndexComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2976
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyViewingIndexComponent, isStandalone: true, selector: "rolatech-property-viewing-index", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings(); as viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\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</rolatech-container>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { 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: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading", "block"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ListComponent, selector: "rolatech-list" }, { kind: "component", type: EmptyComponent, selector: "rolatech-empty" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { 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"] }] });
|
|
2970
2977
|
}
|
|
2971
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type:
|
|
2972
|
-
type:
|
|
2973
|
-
|
|
2978
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyViewingIndexComponent, decorators: [{
|
|
2979
|
+
type: Component,
|
|
2980
|
+
args: [{ selector: 'rolatech-property-viewing-index', imports: [
|
|
2981
|
+
CommonModule,
|
|
2982
|
+
ContainerComponent,
|
|
2983
|
+
RouterModule,
|
|
2984
|
+
TabsComponent,
|
|
2985
|
+
TabComponent,
|
|
2986
|
+
ToolbarComponent,
|
|
2987
|
+
ListComponent,
|
|
2988
|
+
EmptyComponent,
|
|
2989
|
+
MatButtonModule,
|
|
2990
|
+
MatIconModule,
|
|
2991
|
+
FormsModule,
|
|
2992
|
+
MatFormFieldModule,
|
|
2993
|
+
MatDatepickerModule,
|
|
2994
|
+
MatOptionModule,
|
|
2995
|
+
MatInputModule,
|
|
2996
|
+
MatSelectModule,
|
|
2997
|
+
MatButtonModule,
|
|
2998
|
+
PropertyViewingItemComponent,
|
|
2999
|
+
MatPaginatorModule,
|
|
3000
|
+
], template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings(); as viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\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</rolatech-container>\n" }]
|
|
3001
|
+
}] });
|
|
2974
3002
|
|
|
2975
3003
|
class OfferHeaderCard {
|
|
2976
3004
|
ctx = inject(OfferDetailContext);
|
|
@@ -3026,152 +3054,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
3026
3054
|
args: [{ selector: 'rolatech-offer-header-card', imports: [NgClass], template: "@if (ctx.offer(); as o) {\n<section class=\"flex flex-col gap-3 lg:flex-row lg:items-start lg:justify-between\">\n <div class=\"space-y-1\">\n <h1 class=\"text-xl font-semibold text-[--rt-text-primary]\">Offer #{{ o.id }}</h1>\n <div class=\"text-sm text-[--rt-text-secondary]\">\n <span class=\"font-medium\">{{ o.type }}</span>\n <span class=\"mx-2\">\u2022</span>\n <span>Property {{ o.propertyId }}</span>\n </div>\n\n <div class=\"flex flex-wrap gap-2 text-xs\">\n <span class=\"px-2 py-1 rounded-full border bg-[--rt-raised-background]\"> userId: {{ ctx.asText(o.userId) }} </span>\n <span class=\"px-2 py-1 rounded-full border bg-[--rt-raised-background]\"> agentId: {{ ctx.asText(o.agentId) }} </span>\n <span class=\"px-2 py-1 rounded-full border bg-[--rt-raised-background]\">\n invoice: {{ ctx.invoiceOptionLabel[o.invoiceOption] }}\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <span class=\"px-3 py-1 rounded-full border text-xs font-medium\" [ngClass]=\"statusBadgeClass(o.status)\">\n {{ o.status }}\n </span>\n </div>\n</section>\n}\n" }]
|
|
3027
3055
|
}] });
|
|
3028
3056
|
|
|
3029
|
-
class OfferItemCard {
|
|
3030
|
-
ctx = inject(OfferDetailContext);
|
|
3031
|
-
firstMediaUrl(o) {
|
|
3032
|
-
const m = o.item?.media?.[0];
|
|
3033
|
-
return m?.url ?? null;
|
|
3034
|
-
}
|
|
3035
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferItemCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3036
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferItemCard, isStandalone: true, selector: "rolatech-offer-item-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] overflow-hidden\">\n <div class=\"p-4 flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Property item</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">property snapshot</div>\n </div>\n <span class=\"text-xs px-2 py-1 rounded-full border bg-[--rt-base-background]\"> propertyId: {{ o.item.propertyId }} </span>\n </div>\n\n <div class=\"p-4 pt-0 space-y-4\">\n <div class=\"flex flex-col lg:flex-row gap-4\">\n <img\n class=\"w-full lg:w-72 h-44 rounded-xl object-cover bg-gray-100\"\n [src]=\"firstMediaUrl(o) ?? '/assets/placeholder.jpg'\"\n [alt]=\"o.item.title\"\n />\n\n <div class=\"flex-1 space-y-2\">\n <div class=\"text-base font-semibold\">{{ o.item.title }}</div>\n\n <div class=\"grid grid-cols-2 gap-3 text-sm pt-2\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ o.item.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bedrooms</div>\n <div class=\"font-medium\">{{ o.item.bedrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bathrooms</div>\n <div class=\"font-medium\">{{ o.item.bathrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Receptions</div>\n <div class=\"font-medium\">{{ o.item.receptions }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }] });
|
|
3037
|
-
}
|
|
3038
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferItemCard, decorators: [{
|
|
3039
|
-
type: Component,
|
|
3040
|
-
args: [{ selector: 'rolatech-offer-item-card', imports: [PricePipe], template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] overflow-hidden\">\n <div class=\"p-4 flex items-start justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Property item</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">property snapshot</div>\n </div>\n <span class=\"text-xs px-2 py-1 rounded-full border bg-[--rt-base-background]\"> propertyId: {{ o.item.propertyId }} </span>\n </div>\n\n <div class=\"p-4 pt-0 space-y-4\">\n <div class=\"flex flex-col lg:flex-row gap-4\">\n <img\n class=\"w-full lg:w-72 h-44 rounded-xl object-cover bg-gray-100\"\n [src]=\"firstMediaUrl(o) ?? '/assets/placeholder.jpg'\"\n [alt]=\"o.item.title\"\n />\n\n <div class=\"flex-1 space-y-2\">\n <div class=\"text-base font-semibold\">{{ o.item.title }}</div>\n\n <div class=\"grid grid-cols-2 gap-3 text-sm pt-2\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ o.item.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bedrooms</div>\n <div class=\"font-medium\">{{ o.item.bedrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Bathrooms</div>\n <div class=\"font-medium\">{{ o.item.bathrooms }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Receptions</div>\n <div class=\"font-medium\">{{ o.item.receptions }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n}\n" }]
|
|
3041
|
-
}] });
|
|
3042
|
-
|
|
3043
|
-
class OfferRentalTermsCard {
|
|
3044
|
-
ctx = inject(OfferDetailContext);
|
|
3045
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferRentalTermsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3046
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferRentalTermsCard, isStandalone: true, selector: "rolatech-offer-rental-terms-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.rentalTerms</div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : (rt.smoker ? 'Yes' : 'No') }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furnitureRequirement ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None });
|
|
3047
|
-
}
|
|
3048
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferRentalTermsCard, decorators: [{
|
|
3049
|
-
type: Component,
|
|
3050
|
-
args: [{ selector: 'rolatech-offer-rental-terms-card', imports: [PricePipe], encapsulation: ViewEncapsulation.None, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Rental terms</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.rentalTerms</div>\n </div>\n\n @if (o.rentalTerms; as rt) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ rt.amount | price }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Move-in date</div>\n <div class=\"font-medium\">{{ rt.moveInDate ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment frequency</div>\n <div class=\"font-medium\">{{ rt.paymentFrequency ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Tenancy length</div>\n <div class=\"font-medium\">{{ rt.tenancyLengthMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Break clause</div>\n <div class=\"font-medium\">{{ rt.breakClauseMonths ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Pets</div>\n <div class=\"font-medium\">{{ rt.pets.length > 0 ? rt.pets.join(', ') : '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Smoker</div>\n <div class=\"font-medium\">{{ rt.smoker === null ? '\u2014' : (rt.smoker ? 'Yes' : 'No') }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Furniture</div>\n <div class=\"font-medium\">{{ rt.furnitureRequirement ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional requests</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ rt.additionalRequests ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n" }]
|
|
3051
|
-
}] });
|
|
3052
|
-
|
|
3053
|
-
class OfferSaleDetailsCard {
|
|
3054
|
-
ctx = inject(OfferDetailContext);
|
|
3055
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferSaleDetailsCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3056
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferSaleDetailsCard, isStandalone: true, selector: "rolatech-offer-sale-details-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Sale details</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.sale</div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ s.amount ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment method</div>\n <div class=\"font-medium\">{{ s.paymentMethod ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Buyer name</div>\n <div class=\"font-medium\">{{ s.buyerName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ s.phone ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ s.email ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Proposed exchange date</div>\n <div class=\"font-medium\">{{ s.proposedExchangeDate ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Solicitor company</div>\n <div class=\"font-medium\">{{ s.solicitorCompanyName ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n", styles: [""] });
|
|
3057
|
-
}
|
|
3058
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferSaleDetailsCard, decorators: [{
|
|
3059
|
-
type: Component,
|
|
3060
|
-
args: [{ selector: 'rolatech-offer-sale-details-card', imports: [], template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Sale details</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.sale</div>\n </div>\n\n @if (o.sale; as s) {\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Amount</div>\n <div class=\"font-medium\">{{ s.amount ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Payment method</div>\n <div class=\"font-medium\">{{ s.paymentMethod ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Buyer name</div>\n <div class=\"font-medium\">{{ s.buyerName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ s.phone ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ s.email ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Proposed exchange date</div>\n <div class=\"font-medium\">{{ s.proposedExchangeDate ?? '\u2014' }}</div>\n </div>\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Solicitor company</div>\n <div class=\"font-medium\">{{ s.solicitorCompanyName ?? '\u2014' }}</div>\n </div>\n </div>\n }\n</section>\n}\n" }]
|
|
3061
|
-
}] });
|
|
3062
|
-
|
|
3063
|
-
class OfferTenantExpanded {
|
|
3064
|
-
ctx = inject(OfferDetailContext);
|
|
3065
|
-
tenant = input.required(...(ngDevMode ? [{ debugName: "tenant" }] : []));
|
|
3066
|
-
formatAddress(a) {
|
|
3067
|
-
const parts = [a?.line1, a?.line2, a?.city, a?.postcode, a?.country].filter((v) => !!v && v.trim() !== '');
|
|
3068
|
-
return parts.length ? parts.join(', ') : '—';
|
|
3069
|
-
}
|
|
3070
|
-
genderLabel = {
|
|
3071
|
-
MALE: 'Male',
|
|
3072
|
-
FEMALE: 'Female',
|
|
3073
|
-
OTHER: 'Other',
|
|
3074
|
-
PREFER_NOT_TO_SAY: 'Prefer not to say',
|
|
3075
|
-
};
|
|
3076
|
-
guarantor = computed(() => this.tenant()?.guarantor ?? null, ...(ngDevMode ? [{ debugName: "guarantor" }] : []));
|
|
3077
|
-
hasGuarantor = computed(() => !!this.guarantor(), ...(ngDevMode ? [{ debugName: "hasGuarantor" }] : []));
|
|
3078
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantExpanded, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3079
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferTenantExpanded, isStandalone: true, selector: "rolatech-offer-tenant-expanded", inputs: { tenant: { classPropertyName: "tenant", publicName: "tenant", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"p-4 border-t border-[--rt-outline] space-y-4\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Age</div>\n <div class=\"font-medium\">{{ ctx.ageFromDob(tenant().dob) }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Gender</div>\n <div class=\"font-medium\">@if (tenant().gender) { {{ genderLabel[tenant().gender!] }} } @else { \u2014 }</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Applicant type</div>\n <div class=\"font-medium\">{{ tenant().applicantType ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Nationality</div>\n <div class=\"font-medium\">{{ tenant().nationality }}</div>\n </div>\n\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n <div class=\"font-medium\">{{ formatAddress(tenant().address) }}</div>\n </div>\n </div>\n\n @switch (tenant().applicantType) { @case ('INDIVIDUAL') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Individual</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment</div>\n <div class=\"font-medium\">{{ tenant().employmentStatus ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().companyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ tenant().jobTitle ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('CORPORATE') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Corporate</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company number</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyNumber ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('STUDENT') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Student</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">University</div>\n <div class=\"font-medium\">{{ tenant().universityName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Course</div>\n <div class=\"font-medium\">{{ tenant().courseTitle ?? '\u2014' }}</div>\n </div>\n </div>\n } }\n <!-- \u2705 Guarantor -->\n @if (hasGuarantor(); as _unused) { @if (guarantor(); as g) {\n <div class=\"mt-6 rounded-2xl border border-[--rt-outline] p-4\">\n <div class=\"flex items-center justify-between\">\n <div class=\"text-sm font-semibold\">Guarantor</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">{{ g.type }}</div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-12 gap-3 text-sm\">\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Full name</div>\n <div class=\"font-medium\">{{ g.fullName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">DOB</div>\n <div class=\"font-medium\">{{ g.dob ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ g.phone ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ g.email ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n @if (g.address; as a) {\n <div class=\"font-medium\">\n {{ a.line1 ?? '\u2014' }} @if (a.line2) { <span>, {{ a.line2 }}</span> } @if (a.city) { <span>, {{ a.city }}</span> } @if\n (a.postcode) { <span>, {{ a.postcode }}</span> } @if (a.country) { <span>, {{ a.country }}</span> }\n </div>\n } @else {\n <div class=\"font-medium\">\u2014</div>\n }\n </div>\n\n <!-- INDIVIDUAL fields -->\n @if (g.type === 'INDIVIDUAL') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment status</div>\n <div class=\"font-medium\">{{ g.employmentStatus ?? '\u2014' }}</div>\n </div>\n\n @if (g.employmentStatus === 'EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ g.jobTitle ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary ?? '\u2014' }}</div>\n </div>\n } @if (g.employmentStatus === 'SELF_EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary ?? '\u2014' }}</div>\n </div>\n } @if (g.employmentStatus === 'UNEMPLOYED') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional financial support</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ g.additionalFinancialSupport ?? '\u2014' }}</div>\n </div>\n } }\n\n <!-- COMPANY fields (if you later add them on backend) -->\n @if (g.type === 'COMPANY') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company guarantor</div>\n <div class=\"font-medium\">\u2014</div>\n </div>\n }\n </div>\n </div>\n } }\n</div>\n", styles: [""] });
|
|
3080
|
-
}
|
|
3081
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantExpanded, decorators: [{
|
|
3082
|
-
type: Component,
|
|
3083
|
-
args: [{ selector: 'rolatech-offer-tenant-expanded', imports: [], template: "<div class=\"p-4 border-t border-[--rt-outline] space-y-4\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Age</div>\n <div class=\"font-medium\">{{ ctx.ageFromDob(tenant().dob) }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Gender</div>\n <div class=\"font-medium\">@if (tenant().gender) { {{ genderLabel[tenant().gender!] }} } @else { \u2014 }</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Applicant type</div>\n <div class=\"font-medium\">{{ tenant().applicantType ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Nationality</div>\n <div class=\"font-medium\">{{ tenant().nationality }}</div>\n </div>\n\n <div class=\"sm:col-span-2\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n <div class=\"font-medium\">{{ formatAddress(tenant().address) }}</div>\n </div>\n </div>\n\n @switch (tenant().applicantType) { @case ('INDIVIDUAL') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Individual</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment</div>\n <div class=\"font-medium\">{{ tenant().employmentStatus ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().companyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ tenant().jobTitle ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('CORPORATE') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Corporate</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Company number</div>\n <div class=\"font-medium\">{{ tenant().corporateCompanyNumber ?? '\u2014' }}</div>\n </div>\n </div>\n } @case ('STUDENT') {\n <div class=\"rounded-xl border border-[--rt-outline] p-3 space-y-2\">\n <div class=\"text-xs font-semibold\">Student</div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">University</div>\n <div class=\"font-medium\">{{ tenant().universityName ?? '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Course</div>\n <div class=\"font-medium\">{{ tenant().courseTitle ?? '\u2014' }}</div>\n </div>\n </div>\n } }\n <!-- \u2705 Guarantor -->\n @if (hasGuarantor(); as _unused) { @if (guarantor(); as g) {\n <div class=\"mt-6 rounded-2xl border border-[--rt-outline] p-4\">\n <div class=\"flex items-center justify-between\">\n <div class=\"text-sm font-semibold\">Guarantor</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">{{ g.type }}</div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-12 gap-3 text-sm\">\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Full name</div>\n <div class=\"font-medium\">{{ g.fullName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">DOB</div>\n <div class=\"font-medium\">{{ g.dob ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-3\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ g.phone ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Email</div>\n <div class=\"font-medium\">{{ g.email ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Address</div>\n @if (g.address; as a) {\n <div class=\"font-medium\">\n {{ a.line1 ?? '\u2014' }} @if (a.line2) { <span>, {{ a.line2 }}</span> } @if (a.city) { <span>, {{ a.city }}</span> } @if\n (a.postcode) { <span>, {{ a.postcode }}</span> } @if (a.country) { <span>, {{ a.country }}</span> }\n </div>\n } @else {\n <div class=\"font-medium\">\u2014</div>\n }\n </div>\n\n <!-- INDIVIDUAL fields -->\n @if (g.type === 'INDIVIDUAL') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Employment status</div>\n <div class=\"font-medium\">{{ g.employmentStatus ?? '\u2014' }}</div>\n </div>\n\n @if (g.employmentStatus === 'EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Job title</div>\n <div class=\"font-medium\">{{ g.jobTitle ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-4\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary ?? '\u2014' }}</div>\n </div>\n } @if (g.employmentStatus === 'SELF_EMPLOYED') {\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company name</div>\n <div class=\"font-medium\">{{ g.companyName ?? '\u2014' }}</div>\n </div>\n\n <div class=\"col-span-12 lg:col-span-6\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Annual salary</div>\n <div class=\"font-medium\">{{ g.annualSalary ?? '\u2014' }}</div>\n </div>\n } @if (g.employmentStatus === 'UNEMPLOYED') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Additional financial support</div>\n <div class=\"font-medium whitespace-pre-wrap\">{{ g.additionalFinancialSupport ?? '\u2014' }}</div>\n </div>\n } }\n\n <!-- COMPANY fields (if you later add them on backend) -->\n @if (g.type === 'COMPANY') {\n <div class=\"col-span-12\">\n <div class=\"text-xs text-[--rt-text-secondary]\">Company guarantor</div>\n <div class=\"font-medium\">\u2014</div>\n </div>\n }\n </div>\n </div>\n } }\n</div>\n" }]
|
|
3084
|
-
}], propDecorators: { tenant: [{ type: i0.Input, args: [{ isSignal: true, alias: "tenant", required: true }] }] } });
|
|
3085
|
-
|
|
3086
|
-
class OfferTenantCard {
|
|
3087
|
-
// ---- signal inputs
|
|
3088
|
-
index = input.required(...(ngDevMode ? [{ debugName: "index" }] : []));
|
|
3089
|
-
expanded = input.required(...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
3090
|
-
tenant = input.required(...(ngDevMode ? [{ debugName: "tenant" }] : []));
|
|
3091
|
-
// ---- label maps (for tooltips / pretty labels)
|
|
3092
|
-
paymentFrequencyLabel = {
|
|
3093
|
-
MONTHLY: 'Monthly',
|
|
3094
|
-
QUARTERLY: 'Quarterly',
|
|
3095
|
-
SEMI_ANNUALLY: 'Semi-annually',
|
|
3096
|
-
ANNUALLY: 'Annually',
|
|
3097
|
-
};
|
|
3098
|
-
furnitureRequirementLabel = {
|
|
3099
|
-
FURNISHED: 'Furnished',
|
|
3100
|
-
UNFURNISHED: 'Unfurnished',
|
|
3101
|
-
};
|
|
3102
|
-
applicantTypeLabel = {
|
|
3103
|
-
INDIVIDUAL: 'Individual',
|
|
3104
|
-
CORPORATE: 'Corporate',
|
|
3105
|
-
STUDENT: 'Student',
|
|
3106
|
-
};
|
|
3107
|
-
employmentStatusLabel = {
|
|
3108
|
-
EMPLOYED: 'Employed',
|
|
3109
|
-
SELF_EMPLOYED: 'Self-employed',
|
|
3110
|
-
UNEMPLOYED: 'Unemployed',
|
|
3111
|
-
};
|
|
3112
|
-
visaStatusLabel = {
|
|
3113
|
-
CITIZEN: 'Citizen',
|
|
3114
|
-
VISA_HOLDER: 'Visa Holder',
|
|
3115
|
-
WAITING_FOR_VISA: 'Waiting For Visa',
|
|
3116
|
-
NOT_YET_APPLIED_FOR_VISA: 'Not Yet Applied For Visa',
|
|
3117
|
-
};
|
|
3118
|
-
adverseCreditStatusLabel = {
|
|
3119
|
-
NONE: 'None',
|
|
3120
|
-
ACTIVE_CCJ: 'Active Ccj',
|
|
3121
|
-
SATISFIED_CCJ: 'Satisfied Ccj',
|
|
3122
|
-
DEBT_MANAGEMENT_PLAN: 'Debt Management Plan',
|
|
3123
|
-
LOW_CREDIT_SCORE: 'Low Credit Score',
|
|
3124
|
-
OTHER: 'Other',
|
|
3125
|
-
};
|
|
3126
|
-
// ---- callback input instead of Output
|
|
3127
|
-
onToggleInput = input.required(...(ngDevMode ? [{ debugName: "onToggleInput" }] : []));
|
|
3128
|
-
onToggle() {
|
|
3129
|
-
this.onToggleInput()(this.index());
|
|
3130
|
-
}
|
|
3131
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3132
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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 } }, ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-outline] bg-[--rt-base-background] overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-start justify-between gap-3 hover:bg-[--rt-raised-background]\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-sm font-semibold truncate\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"text-xs text-[--rt-text-secondary] truncate\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"flex flex-wrap gap-2 pt-1 text-xs\">\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <span class=\"text-xs px-2 py-1 rounded-full border\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span>\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n }\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: OfferTenantExpanded, selector: "rolatech-offer-tenant-expanded", inputs: ["tenant"] }] });
|
|
3133
|
-
}
|
|
3134
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantCard, decorators: [{
|
|
3135
|
-
type: Component,
|
|
3136
|
-
args: [{ selector: 'rolatech-offer-tenant-card', imports: [OfferTenantExpanded], template: "<div class=\"rounded-xl border border-[--rt-outline] bg-[--rt-base-background] overflow-hidden\">\n <button\n type=\"button\"\n class=\"w-full text-left p-4 flex items-start justify-between gap-3 hover:bg-[--rt-raised-background]\"\n (click)=\"onToggle()\"\n >\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-sm font-semibold truncate\">{{ tenant().fullName ?? '--' }}</div>\n <div class=\"text-xs text-[--rt-text-secondary] truncate\">{{ tenant().email ?? '--' }} \u00B7 {{ tenant().phone ?? '--' }}</div>\n <div class=\"flex flex-wrap gap-2 pt-1 text-xs\">\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().applicantType) { {{ applicantTypeLabel[tenant().applicantType!] }} } @else { Applicant: -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().employmentStatus) { {{ employmentStatusLabel[tenant().employmentStatus!] }} } @else { Employment: --}\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().adverseCreditStatus) { {{ adverseCreditStatusLabel[tenant().adverseCreditStatus!] }} } @else { Credit:\n -- }\n </span>\n\n <span class=\"px-2 py-1 rounded-full border\">\n @if (tenant().visaStatus) { {{ visaStatusLabel[tenant().visaStatus!] }} } @else { Visa: \u2014 }\n </span>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2 shrink-0\">\n <span class=\"text-xs px-2 py-1 rounded-full border\"> {{ expanded() ? 'Expanded' : 'Collapsed' }} </span>\n <svg class=\"w-4 h-4 transition-transform\" [class.rotate-180]=\"expanded()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fill-rule=\"evenodd\"\n d=\"M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.168l3.71-3.936a.75.75 0 1 1 1.08 1.04l-4.24 4.5a.75.75 0 0 1-1.08 0l-4.24-4.5a.75.75 0 0 1 .02-1.06z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n </div>\n </button>\n\n @if (expanded()) {\n <rolatech-offer-tenant-expanded [tenant]=\"tenant()\" />\n }\n</div>\n" }]
|
|
3137
|
-
}], 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 }] }] } });
|
|
3138
|
-
|
|
3139
|
-
class OfferTenantsAccordion {
|
|
3140
|
-
ctx = inject(OfferDetailContext);
|
|
3141
|
-
// tenants snapshot from ctx
|
|
3142
|
-
tenants = computed(() => this.ctx.offer()?.tenants ?? [], ...(ngDevMode ? [{ debugName: "tenants" }] : []));
|
|
3143
|
-
// index-based expanded state (per your requirement)
|
|
3144
|
-
expandedIndexes = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedIndexes" }] : []));
|
|
3145
|
-
isExpanded(i) {
|
|
3146
|
-
return this.expandedIndexes().has(i);
|
|
3147
|
-
}
|
|
3148
|
-
toggle = (index) => {
|
|
3149
|
-
this.expandedIndexes.update((prev) => {
|
|
3150
|
-
const next = new Set(prev);
|
|
3151
|
-
if (next.has(index))
|
|
3152
|
-
next.delete(index);
|
|
3153
|
-
else
|
|
3154
|
-
next.add(index);
|
|
3155
|
-
return next;
|
|
3156
|
-
});
|
|
3157
|
-
};
|
|
3158
|
-
expandAll(count) {
|
|
3159
|
-
const next = new Set();
|
|
3160
|
-
for (let i = 0; i < count; i++)
|
|
3161
|
-
next.add(i);
|
|
3162
|
-
this.expandedIndexes.set(next);
|
|
3163
|
-
}
|
|
3164
|
-
collapseAll() {
|
|
3165
|
-
this.expandedIndexes.set(new Set());
|
|
3166
|
-
}
|
|
3167
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantsAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3168
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferTenantsAccordion, isStandalone: true, selector: "rolatech-offer-tenants-accordion", ngImport: i0, template: "@if (tenants(); as ts) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Tenants</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.tenants</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border bg-[--rt-base-background]\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-[--rt-text-secondary]\">No tenants</div>\n }\n</section>\n}\n", styles: [""], dependencies: [{ kind: "component", type: OfferTenantCard, selector: "rolatech-offer-tenant-card", inputs: ["index", "expanded", "tenant", "onToggleInput"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
3169
|
-
}
|
|
3170
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferTenantsAccordion, decorators: [{
|
|
3171
|
-
type: Component,
|
|
3172
|
-
args: [{ selector: 'rolatech-offer-tenants-accordion', imports: [OfferTenantCard], encapsulation: ViewEncapsulation.None, template: "@if (tenants(); as ts) {\n<section class=\"rounded-2xl border border-[--rt-outline] bg-[--rt-raised-background] p-4 space-y-3\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <div class=\"text-sm font-semibold\">Tenants</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">o.tenants</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border bg-[--rt-base-background]\"\n (click)=\"expandAll(ts.length)\"\n [disabled]=\"ts.length === 0\"\n >\n Expand all\n </button>\n <button\n type=\"button\"\n class=\"text-xs px-3 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n (click)=\"collapseAll()\"\n [disabled]=\"expandedIndexes().size === 0\"\n >\n Collapse all\n </button>\n <span class=\"text-xs px-2 py-1 rounded-full border border-[--rt-outline] bg-[--rt-base-background]\"\n >count: {{ ts.length }}</span\n >\n </div>\n </div>\n\n @if (ts.length > 0) {\n <div class=\"space-y-3\">\n @for (t of ts; track $index) {\n <rolatech-offer-tenant-card [index]=\"$index\" [expanded]=\"isExpanded($index)\" [tenant]=\"t\" [onToggleInput]=\"toggle\" />\n }\n </div>\n } @else {\n <div class=\"text-sm text-[--rt-text-secondary]\">No tenants</div>\n }\n</section>\n}\n" }]
|
|
3173
|
-
}] });
|
|
3174
|
-
|
|
3175
3057
|
class OfferSideSummaryCard {
|
|
3176
3058
|
ctx = inject(OfferDetailContext);
|
|
3177
3059
|
salePaymentMethodLabel = {
|
|
@@ -3179,11 +3061,11 @@ class OfferSideSummaryCard {
|
|
|
3179
3061
|
MORTGAGE: 'Mortgage',
|
|
3180
3062
|
};
|
|
3181
3063
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferSideSummaryCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3182
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline]
|
|
3064
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: OfferSideSummaryCard, isStandalone: true, selector: "rolatech-offer-side-summary-card", host: { classAttribute: "block" }, ngImport: i0, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Summary</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Key fields</div>\n </div>\n\n <div class=\"grid grid-cols-1 gap-3 text-sm\">\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Type</span><span class=\"font-medium\">{{ o.type }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Status</span><span class=\"font-medium\">{{ o.status }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Invoice</span\n ><span class=\"font-medium\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Tenants</span><span class=\"font-medium\">{{ o.tenants?.length ?? 0 }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Item amount</span><span class=\"font-medium\">{{ o.item.amount | price }}</span>\n </div>\n @if (ctx.isRental()) {\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-[--rt-text-secondary]\">Tenants</span>\n <span class=\"font-medium\">{{ ctx.asText(o.tenants?.length ?? 0) }}</span>\n </div>\n }\n </div>\n</section>\n}\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }] });
|
|
3183
3065
|
}
|
|
3184
3066
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: OfferSideSummaryCard, decorators: [{
|
|
3185
3067
|
type: Component,
|
|
3186
|
-
args: [{ selector: 'rolatech-offer-side-summary-card', imports: [PricePipe], template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline]
|
|
3068
|
+
args: [{ selector: 'rolatech-offer-side-summary-card', imports: [PricePipe], host: { class: 'block' }, template: "@if (ctx.offer(); as o) {\n<section class=\"rounded-2xl border border-[--rt-outline] p-4 space-y-3\">\n <div>\n <div class=\"text-sm font-semibold\">Summary</div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Key fields</div>\n </div>\n\n <div class=\"grid grid-cols-1 gap-3 text-sm\">\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Type</span><span class=\"font-medium\">{{ o.type }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Status</span><span class=\"font-medium\">{{ o.status }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Invoice</span\n ><span class=\"font-medium\">{{ ctx.invoiceOptionLabel[o.invoiceOption] }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Tenants</span><span class=\"font-medium\">{{ o.tenants?.length ?? 0 }}</span>\n </div>\n <div class=\"flex items-center justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Item amount</span><span class=\"font-medium\">{{ o.item.amount | price }}</span>\n </div>\n @if (ctx.isRental()) {\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-[--rt-text-secondary]\">Tenants</span>\n <span class=\"font-medium\">{{ ctx.asText(o.tenants?.length ?? 0) }}</span>\n </div>\n }\n </div>\n</section>\n}\n" }]
|
|
3187
3069
|
}] });
|
|
3188
3070
|
|
|
3189
3071
|
class PropertyOfferDetailComponent extends BaseComponent {
|
|
@@ -3344,7 +3226,7 @@ class PropertyOfferDetailComponent extends BaseComponent {
|
|
|
3344
3226
|
});
|
|
3345
3227
|
}
|
|
3346
3228
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyOfferDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
3347
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", providers: [OfferDetailContext], usesInheritance: true, ngImport: i0, template: "@if (ctx.offer(); as o) {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-6 space-y-
|
|
3229
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyOfferDetailComponent, isStandalone: true, selector: "rolatech-property-offer-detail", providers: [OfferDetailContext], usesInheritance: true, ngImport: i0, template: "@if (ctx.offer(); as o) {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-6 space-y-4\">\n <rolatech-offer-header-card />\n\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4\">\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4\">\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n <rolatech-offer-rental-terms-card />\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n </div>\n\n <aside class=\"lg:col-span-4 space-y-4\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n </div>\n} @else {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-10\">\n <div class=\"rounded-2xl border bg-[--rolatech-raised-background] p-6\">Loading\u2026</div>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: OfferHeaderCard, selector: "rolatech-offer-header-card" }, { kind: "component", type: OfferItemCard, selector: "rolatech-offer-item-card" }, { kind: "component", type: OfferRentalTermsCard, selector: "rolatech-offer-rental-terms-card" }, { kind: "component", type: OfferSaleDetailsCard, selector: "rolatech-offer-sale-details-card" }, { kind: "component", type: OfferTenantsAccordion, selector: "rolatech-offer-tenants-accordion" }, { kind: "component", type: OfferSideSummaryCard, selector: "rolatech-offer-side-summary-card" }] });
|
|
3348
3230
|
}
|
|
3349
3231
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyOfferDetailComponent, decorators: [{
|
|
3350
3232
|
type: Component,
|
|
@@ -3358,7 +3240,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
3358
3240
|
OfferSaleDetailsCard,
|
|
3359
3241
|
OfferTenantsAccordion,
|
|
3360
3242
|
OfferSideSummaryCard,
|
|
3361
|
-
], providers: [OfferDetailContext], template: "@if (ctx.offer(); as o) {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-6 space-y-
|
|
3243
|
+
], providers: [OfferDetailContext], template: "@if (ctx.offer(); as o) {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-6 space-y-4\">\n <rolatech-offer-header-card />\n\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-4\">\n <div class=\"grid grid-cols-1 lg:col-span-8 space-y-4\">\n <rolatech-offer-item-card />\n\n @if (ctx.isRental()) {\n <rolatech-offer-rental-terms-card />\n <rolatech-offer-tenants-accordion />\n }\n\n @if (ctx.isSale()) {\n <rolatech-offer-sale-details-card />\n }\n </div>\n\n <aside class=\"lg:col-span-4 space-y-4\">\n <rolatech-offer-side-summary-card />\n </aside>\n </div>\n </div>\n} @else {\n <div class=\"max-w-7xl mx-auto px-4 lg:px-6 py-10\">\n <div class=\"rounded-2xl border bg-[--rolatech-raised-background] p-6\">Loading\u2026</div>\n </div>\n}\n" }]
|
|
3362
3244
|
}] });
|
|
3363
3245
|
|
|
3364
3246
|
class PropertyViewingDetailComponent extends BaseComponent {
|
|
@@ -3908,7 +3790,7 @@ class RentalOfferTermsSection {
|
|
|
3908
3790
|
deps: [MAT_DATE_LOCALE],
|
|
3909
3791
|
},
|
|
3910
3792
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
|
|
3911
|
-
], ngImport: i0, template: "<div class=\"grid\" [formGroup]=\"rental\">\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\" formControlName=\"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\" formControlName=\"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 formControlName=\"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\" formControlName=\"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\" formControlName=\"breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select formControlName=\"furnitureRequirement\">\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
|
|
3793
|
+
], ngImport: i0, template: "<div class=\"grid\" [formGroup]=\"rental\">\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\" formControlName=\"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\" formControlName=\"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 formControlName=\"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\" formControlName=\"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\" formControlName=\"breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select formControlName=\"furnitureRequirement\">\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-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of 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 placeholder=\"Add a pet (e.g., Dog, Cat)\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n @if (petsControl().value.length) {\n <p class=\"hint\">Landlord approval may still be required.</p>\n }\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" formControlName=\"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\" formControlName=\"additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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.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: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.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.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: i1$4.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i1$4.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i1$4.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i1$4.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: AngularComponentsModule }] });
|
|
3912
3794
|
}
|
|
3913
3795
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: RentalOfferTermsSection, decorators: [{
|
|
3914
3796
|
type: Component,
|
|
@@ -3930,7 +3812,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
3930
3812
|
deps: [MAT_DATE_LOCALE],
|
|
3931
3813
|
},
|
|
3932
3814
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS$5 },
|
|
3933
|
-
], template: "<div class=\"grid\" [formGroup]=\"rental\">\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\" formControlName=\"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\" formControlName=\"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 formControlName=\"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\" formControlName=\"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\" formControlName=\"breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select formControlName=\"furnitureRequirement\">\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
|
|
3815
|
+
], template: "<div class=\"grid\" [formGroup]=\"rental\">\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\" formControlName=\"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\" formControlName=\"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 formControlName=\"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\" formControlName=\"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\" formControlName=\"breakClauseMonths\" />\n </mat-form-field>\n <mat-form-field class=\"col-span-4\">\n <mat-label>Furniture</mat-label>\n <mat-select formControlName=\"furnitureRequirement\">\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-chip-grid #chipGrid aria-label=\"Pets\">\n @for (p of 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 placeholder=\"Add a pet (e.g., Dog, Cat)\"\n [matChipInputFor]=\"chipGrid\"\n [matChipInputSeparatorKeyCodes]=\"separatorKeysCodes\"\n (matChipInputTokenEnd)=\"addPet($event)\"\n />\n </mat-chip-grid>\n\n <mat-hint>Press Enter or comma to add multiple pets.</mat-hint>\n </mat-form-field>\n\n @if (petsControl().value.length) {\n <p class=\"hint\">Landlord approval may still be required.</p>\n }\n\n <mat-slide-toggle labelPosition=\"before\" class=\"py-3\" formControlName=\"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\" formControlName=\"additionalRequests\"></textarea>\n </mat-form-field>\n</div>\n" }]
|
|
3934
3816
|
}] });
|
|
3935
3817
|
|
|
3936
3818
|
class TenantRegistryService {
|
|
@@ -4258,11 +4140,11 @@ class TenantListSection {
|
|
|
4258
4140
|
return g.get('fullName')?.value || '—';
|
|
4259
4141
|
}
|
|
4260
4142
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantListSection, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4261
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TenantListSection, isStandalone: true, selector: "rolatech-tenant-list-section", providers: [TenantRegistryService], ngImport: i0, template: "<div class=\"text-xl py-
|
|
4143
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TenantListSection, isStandalone: true, selector: "rolatech-tenant-list-section", providers: [TenantRegistryService], ngImport: i0, template: "<div class=\"text-xl py-2\">Tenants</div>\n<mat-accordion multi>\n @for (key of keys(); track key) {\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>Tenant {{ $index + 1 }}</mat-panel-title>\n <mat-panel-description>{{ tenantName(key) }}</mat-panel-description>\n </mat-expansion-panel-header>\n <rolatech-tenant-card [clientKey]=\"key\" />\n </mat-expansion-panel>\n }\n</mat-accordion>\n<div class=\"py-3\">\n <button mat-stroked-button type=\"button\" (click)=\"addTenant()\">\n <mat-icon>add</mat-icon>\n <span>Add tenant</span>\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i2$4.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i2$4.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i2$4.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i2$4.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i2$4.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "component", type: TenantCard, selector: "rolatech-tenant-card", inputs: ["clientKey"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
|
|
4262
4144
|
}
|
|
4263
4145
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TenantListSection, decorators: [{
|
|
4264
4146
|
type: Component,
|
|
4265
|
-
args: [{ selector: 'rolatech-tenant-list-section', imports: [MatButtonModule, MatExpansionModule, TenantCard], providers: [TenantRegistryService], template: "<div class=\"text-xl py-
|
|
4147
|
+
args: [{ selector: 'rolatech-tenant-list-section', imports: [MatButtonModule, MatExpansionModule, TenantCard, MatIcon], providers: [TenantRegistryService], template: "<div class=\"text-xl py-2\">Tenants</div>\n<mat-accordion multi>\n @for (key of keys(); track key) {\n <mat-expansion-panel>\n <mat-expansion-panel-header>\n <mat-panel-title>Tenant {{ $index + 1 }}</mat-panel-title>\n <mat-panel-description>{{ tenantName(key) }}</mat-panel-description>\n </mat-expansion-panel-header>\n <rolatech-tenant-card [clientKey]=\"key\" />\n </mat-expansion-panel>\n }\n</mat-accordion>\n<div class=\"py-3\">\n <button mat-stroked-button type=\"button\" (click)=\"addTenant()\">\n <mat-icon>add</mat-icon>\n <span>Add tenant</span>\n </button>\n</div>\n" }]
|
|
4266
4148
|
}], ctorParameters: () => [] });
|
|
4267
4149
|
|
|
4268
4150
|
class PropertyOfferRentalForm {
|
|
@@ -4309,7 +4191,7 @@ class PropertyOfferCreate {
|
|
|
4309
4191
|
facade = inject(OfferFormFacade);
|
|
4310
4192
|
titleService = inject(TitleService);
|
|
4311
4193
|
propertyService = inject(PropertyService);
|
|
4312
|
-
offerTypeLabel = computed(() => (this.facade.offerType() === 'RENTAL' ? '
|
|
4194
|
+
offerTypeLabel = computed(() => (this.facade.offerType() === 'RENTAL' ? 'rental offer' : 'sale offer'), ...(ngDevMode ? [{ debugName: "offerTypeLabel" }] : []));
|
|
4313
4195
|
property = signal(null, ...(ngDevMode ? [{ debugName: "property" }] : []));
|
|
4314
4196
|
firstImageUrl = computed(() => {
|
|
4315
4197
|
const p = this.property();
|
|
@@ -4350,7 +4232,7 @@ class PropertyOfferCreate {
|
|
|
4350
4232
|
// await this.router.navigate(['../', res.id], { relativeTo: this.route });
|
|
4351
4233
|
}
|
|
4352
4234
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyOfferCreate, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4353
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-
|
|
4235
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: PropertyOfferCreate, isStandalone: true, selector: "rolatech-property-offer-create", providers: [OfferFormFacade], ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-4 gap-3\">\n <div class=\"w-full md:w-3/4\">\n <div class=\"w-full\">\n <div class=\"text-xl font-bold mb-3\">Create {{ offerTypeLabel() }}</div>\n\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 <button class=\"w-full md:w-1/2\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{facade.submitting() ? 'Submitting' : 'Submit'}}\n </button>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n </div>\n <div class=\"w-full md:w-1/4\">\n @if (property()) {\n <div class=\"text-xl font-bold mb-3\" i18n>Property info</div>\n <div>\n <rolatech-thumbnail [src]=\"firstImageUrl()\" size=\"small\"></rolatech-thumbnail>\n </div>\n <div class=\"text-xl font-bold\">{{ property()!.title }}</div>\n <div class=\"\">Available: {{ property()!.availableDate | availableDate }}</div>\n @if (property()!.market.toString() === 'LETTINGS' && property()!.rentFrequency.toString() === 'MONTHLY') {\n <div class=\"py-3 text-xl font-bold\">{{ property()!.price | price }}pcm</div>\n } @else {\n <div class=\"py-3 text-xl font-bold\">{{ property()!.price | price }}</div>\n } }\n </div>\n </div>\n</rolatech-container>\n", styles: [""], dependencies: [{ kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: ThumbnailComponent, selector: "rolatech-thumbnail", inputs: ["src", "size", "mode", "ratio", "width", "height"] }, { kind: "ngmodule", type: MatStepperModule }, { 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" }, { kind: "component", type: PropertyOfferSaleForm, selector: "rolatech-property-offer-sale-form" }, { kind: "pipe", type: PricePipe, name: "price" }, { kind: "pipe", type: AvailableDatePipe, name: "availableDate" }] });
|
|
4354
4236
|
}
|
|
4355
4237
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: PropertyOfferCreate, decorators: [{
|
|
4356
4238
|
type: Component,
|
|
@@ -4364,7 +4246,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
4364
4246
|
MatSnackBarModule,
|
|
4365
4247
|
PropertyOfferRentalForm,
|
|
4366
4248
|
PropertyOfferSaleForm,
|
|
4367
|
-
], providers: [OfferFormFacade], template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-
|
|
4249
|
+
], providers: [OfferFormFacade], template: "<rolatech-container>\n <div class=\"flex flex-col-reverse md:flex-row w-full py-4 gap-3\">\n <div class=\"w-full md:w-3/4\">\n <div class=\"w-full\">\n <div class=\"text-xl font-bold mb-3\">Create {{ offerTypeLabel() }}</div>\n\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 <button class=\"w-full md:w-1/2\" mat-flat-button [disabled]=\"facade.submitting()\" (click)=\"submit()\">\n {{facade.submitting() ? 'Submitting' : 'Submit'}}\n </button>\n\n @if (facade.submitError()) {\n <p class=\"error\">{{ facade.submitError() }}</p>\n }\n </div>\n </div>\n <div class=\"w-full md:w-1/4\">\n @if (property()) {\n <div class=\"text-xl font-bold mb-3\" i18n>Property info</div>\n <div>\n <rolatech-thumbnail [src]=\"firstImageUrl()\" size=\"small\"></rolatech-thumbnail>\n </div>\n <div class=\"text-xl font-bold\">{{ property()!.title }}</div>\n <div class=\"\">Available: {{ property()!.availableDate | availableDate }}</div>\n @if (property()!.market.toString() === 'LETTINGS' && property()!.rentFrequency.toString() === 'MONTHLY') {\n <div class=\"py-3 text-xl font-bold\">{{ property()!.price | price }}pcm</div>\n } @else {\n <div class=\"py-3 text-xl font-bold\">{{ property()!.price | price }}</div>\n } }\n </div>\n </div>\n</rolatech-container>\n" }]
|
|
4368
4250
|
}], ctorParameters: () => [] });
|
|
4369
4251
|
|
|
4370
4252
|
const propertyRoutes = [
|
|
@@ -4374,7 +4256,7 @@ const propertyRoutes = [
|
|
|
4374
4256
|
children: [
|
|
4375
4257
|
{
|
|
4376
4258
|
path: '',
|
|
4377
|
-
loadComponent: () => import('./rolatech-angular-property-property-index.component-
|
|
4259
|
+
loadComponent: () => import('./rolatech-angular-property-property-index.component-BSsr0oLt.mjs').then((x) => x.PropertyIndexComponent),
|
|
4378
4260
|
},
|
|
4379
4261
|
],
|
|
4380
4262
|
},
|
|
@@ -4408,13 +4290,9 @@ const propertyRoutes = [
|
|
|
4408
4290
|
canActivate: [AuthGuard],
|
|
4409
4291
|
component: PropertyViewingRequestComponent,
|
|
4410
4292
|
},
|
|
4411
|
-
{
|
|
4412
|
-
path: ':id/offer',
|
|
4413
|
-
canActivate: [AuthGuard],
|
|
4414
|
-
component: PropertyOfferComponent,
|
|
4415
|
-
},
|
|
4416
4293
|
{
|
|
4417
4294
|
path: ':id/offer/:type', // type = rental | sale
|
|
4295
|
+
canActivate: [AuthGuard],
|
|
4418
4296
|
component: PropertyOfferCreate,
|
|
4419
4297
|
},
|
|
4420
4298
|
{
|
|
@@ -6289,7 +6167,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
6289
6167
|
const propertyManageViewingsRoutes = [
|
|
6290
6168
|
{
|
|
6291
6169
|
path: '',
|
|
6292
|
-
loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-
|
|
6170
|
+
loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-gp5KsBTB.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
|
|
6293
6171
|
},
|
|
6294
6172
|
{
|
|
6295
6173
|
path: ':id',
|
|
@@ -7356,9 +7234,9 @@ class PropertyAgentOfferIndex extends BaseComponent {
|
|
|
7356
7234
|
icon: 'dashboard',
|
|
7357
7235
|
},
|
|
7358
7236
|
{
|
|
7359
|
-
name: '
|
|
7237
|
+
name: 'Submitted',
|
|
7360
7238
|
icon: 'category',
|
|
7361
|
-
status: '
|
|
7239
|
+
status: 'submitted',
|
|
7362
7240
|
},
|
|
7363
7241
|
{
|
|
7364
7242
|
name: 'Accepted',
|
|
@@ -7478,4 +7356,4 @@ const propertyManageOverviewRoutes = [
|
|
|
7478
7356
|
*/
|
|
7479
7357
|
|
|
7480
7358
|
export { PropertyOfferTimelineStatus as A, PropertyOfferType as B, PropertyOfferStatus as C, ResidencyStatus as D, EmploymentStatus as E, PropertyApplicantType as F, AdverseCreditStatus as G, BedroomRange as H, PriceRange as I, Market as M, 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, propertyAgentViewingRoutes as i, propertyAgentOfferRoutes as j, propertyAgentOverviewRoutes as k, lettingManageRoutes as l, propertyManageOverviewRoutes as m, PropertyActionsComponent as n, PropertyItemComponent as o, propertyReviewRoutes as p, PropertyPricingComponent as q, PropertyPriceType as r, PropertyVideoProvider as s, tenantManageRoutes as t, PropertyStatus as u, PropertyType as v, PropertyInventoryStatus as w, PropertyScope as x, PropertyViewingStatus as y, PropertyViewerCategory as z };
|
|
7481
|
-
//# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-
|
|
7359
|
+
//# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-JGdXP_pX.mjs.map
|