@rolatech/angular-property 20.0.2-beta.2 → 20.0.3

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.
Files changed (17) hide show
  1. package/fesm2022/rolatech-angular-property-property-filter-icon.component-_P3G8E-x.mjs +126 -0
  2. package/fesm2022/rolatech-angular-property-property-filter-icon.component-_P3G8E-x.mjs.map +1 -0
  3. package/fesm2022/rolatech-angular-property-property-index.component-CbbxIQ8w.mjs +92 -0
  4. package/fesm2022/rolatech-angular-property-property-index.component-CbbxIQ8w.mjs.map +1 -0
  5. package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-gVG8HPPp.mjs → rolatech-angular-property-property-manage-viewings-index.component-Bk02i8QS.mjs} +2 -2
  6. package/fesm2022/{rolatech-angular-property-property-manage-viewings-index.component-gVG8HPPp.mjs.map → rolatech-angular-property-property-manage-viewings-index.component-Bk02i8QS.mjs.map} +1 -1
  7. package/fesm2022/{rolatech-angular-property-rolatech-angular-property-B4w7sAFv.mjs → rolatech-angular-property-rolatech-angular-property-nGHNbG8j.mjs} +580 -168
  8. package/fesm2022/rolatech-angular-property-rolatech-angular-property-nGHNbG8j.mjs.map +1 -0
  9. package/fesm2022/rolatech-angular-property.mjs +1 -1
  10. package/index.d.ts +25 -17
  11. package/package.json +5 -5
  12. package/themes/_default.scss +1 -1
  13. package/fesm2022/rolatech-angular-property-property-filter-icon.component-CO2B-Na5.mjs +0 -93
  14. package/fesm2022/rolatech-angular-property-property-filter-icon.component-CO2B-Na5.mjs.map +0 -1
  15. package/fesm2022/rolatech-angular-property-property-index.component-Birv1xyB.mjs +0 -43
  16. package/fesm2022/rolatech-angular-property-property-index.component-Birv1xyB.mjs.map +0 -1
  17. package/fesm2022/rolatech-angular-property-rolatech-angular-property-B4w7sAFv.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, model, signal, Pipe, ViewChild } from '@angular/core';
2
+ import { inject, ElementRef, input, booleanAttribute, effect, HostBinding, ViewEncapsulation, Component, viewChild, PLATFORM_ID, output, computed, EventEmitter, Injectable, signal, Pipe, model, ViewChild } from '@angular/core';
3
3
  import * as i1$4 from '@angular/material/button';
4
4
  import { MatButtonModule } from '@angular/material/button';
5
5
  import * as i4 from '@angular/material/menu';
@@ -10,11 +10,11 @@ import { MatSnackBar } from '@angular/material/snack-bar';
10
10
  import { MatTableModule } from '@angular/material/table';
11
11
  import * as i1$1 from '@angular/router';
12
12
  import { RouterLink, RouterModule, Router, ActivatedRoute, RouterLinkActive, RouterOutlet } from '@angular/router';
13
- import { ThumbnailComponent, ImagePlaceholderComponent, ToolbarComponent, SpinnerComponent, ImagePreviewDialogComponent, BaseComponent, AcceptDialogComponent, RejectDialogComponent, RichLabelComponent, AngularComponentsModule, ConfirmationDialogComponent, TabsComponent, TabComponent, EmptyComponent, ContainerComponent, ListComponent, MediaListComponent, MediaListItemComponent, VideoUpload } from '@rolatech/angular-components';
14
- import { PropertyService, FeatureService } from '@rolatech/angular-services';
13
+ import { ThumbnailComponent, ImagePlaceholderComponent, ToolbarComponent, SpinnerComponent, ImagePreviewDialogComponent, BaseComponent, AcceptDialogComponent, RejectDialogComponent, RichLabelComponent, AngularComponentsModule, ConfirmationDialogComponent, TabsComponent, TabComponent, EmptyComponent, ContainerComponent, SearchBar, ListComponent, MediaListComponent, MediaListItemComponent, InputComponent } from '@rolatech/angular-components';
14
+ import { PropertyService, FeatureService, PropertySearchService, DialogService } from '@rolatech/angular-services';
15
15
  import * as i1 from '@angular/common';
16
16
  import { CommonModule, NgClass, isPlatformBrowser, ViewportScroller, KeyValuePipe, Location } from '@angular/common';
17
- import { FixedPipe, AngularCommonModule, OptionsFormatPipe, APP_CONFIG, DecimalDirective } from '@rolatech/angular-common';
17
+ import { FixedPipe, AngularCommonModule, OptionsFormatPipe, APP_CONFIG, DecimalDirective, DurationPipe } from '@rolatech/angular-common';
18
18
  import * as i2 from '@angular/material/icon';
19
19
  import { MatIconModule, MatIcon } from '@angular/material/icon';
20
20
  import { MatDialog } from '@angular/material/dialog';
@@ -38,9 +38,6 @@ import { MatOptionModule, MAT_DATE_LOCALE, DateAdapter, MAT_DATE_FORMATS } from
38
38
  import _, { findIndex, findLastIndex, first, remove } from 'lodash';
39
39
  import * as i2$1 from '@angular/material/divider';
40
40
  import { MatDividerModule, MatDivider } from '@angular/material/divider';
41
- import * as i7 from '@angular/material/datepicker';
42
- import { MatDatepickerModule } from '@angular/material/datepicker';
43
- import { trigger, state, transition, style, animate } from '@angular/animations';
44
41
  import { CommentsComponent } from '@rolatech/angular-comment';
45
42
  import { DomSanitizer } from '@angular/platform-browser';
46
43
  import * as i4$1 from '@angular/cdk/text-field';
@@ -48,8 +45,11 @@ import { TextFieldModule } from '@angular/cdk/text-field';
48
45
  import * as i7$1 from '@angular/material/radio';
49
46
  import { MatRadioModule } from '@angular/material/radio';
50
47
  import { MomentDateAdapter } from '@angular/material-moment-adapter';
48
+ import * as i7 from '@angular/material/datepicker';
49
+ import { MatDatepickerModule } from '@angular/material/datepicker';
51
50
  import * as i3$1 from '@angular/material/progress-spinner';
52
51
  import { MatProgressSpinner, MatProgressSpinnerModule } from '@angular/material/progress-spinner';
52
+ import { trigger, state, transition, style, animate } from '@angular/animations';
53
53
  import * as i8 from '@angular/material/progress-bar';
54
54
  import { MatProgressBarModule } from '@angular/material/progress-bar';
55
55
  import * as i1$6 from '@angular/material/checkbox';
@@ -61,13 +61,13 @@ var PropertyPriceType;
61
61
  PropertyPriceType["PARTIAL"] = "\u652F\u4ED8\u5B9A\u91D1";
62
62
  PropertyPriceType["VARIED"] = "\u591A\u53D8\u7684";
63
63
  })(PropertyPriceType || (PropertyPriceType = {}));
64
- var PropertyVideoTourType;
65
- (function (PropertyVideoTourType) {
66
- PropertyVideoTourType["YOUTUBE"] = "Youtube";
67
- PropertyVideoTourType["S3"] = "AWS S3";
68
- PropertyVideoTourType["LOCAL"] = "Local server";
69
- PropertyVideoTourType["COS"] = "COS";
70
- })(PropertyVideoTourType || (PropertyVideoTourType = {}));
64
+ var PropertyVideoProvider;
65
+ (function (PropertyVideoProvider) {
66
+ PropertyVideoProvider["YOUTUBE"] = "YOUTUBE";
67
+ PropertyVideoProvider["S3"] = "AWS S3";
68
+ PropertyVideoProvider["LOCAL"] = "Local server";
69
+ PropertyVideoProvider["COS"] = "COS";
70
+ })(PropertyVideoProvider || (PropertyVideoProvider = {}));
71
71
  var PropertyStatus;
72
72
  (function (PropertyStatus) {
73
73
  PropertyStatus[PropertyStatus["DRAFT"] = 'Draft'] = "DRAFT";
@@ -1067,83 +1067,78 @@ const propertyManageOffersRoutes = [
1067
1067
  },
1068
1068
  ];
1069
1069
 
1070
- class PropertyFilterComponent {
1071
- towns = ['London'];
1072
- filter = model(false, ...(ngDevMode ? [{ debugName: "filter" }] : []));
1073
- minDate = new Date();
1074
- propertyFilterIconComponent = defer(() => import('./rolatech-angular-property-property-filter-icon.component-CO2B-Na5.mjs').then((m) => m.PropertyFilterIconComponent));
1075
- searchOptions = {
1076
- town: '',
1077
- startDate: '',
1078
- endDate: '',
1079
- };
1080
- reset() { }
1081
- search() { }
1082
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1083
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyFilterComponent, isStandalone: true, selector: "rolatech-property-filter", inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filter: "filterChange" }, ngImport: i0, template: "<!-- <div class=\"p-3\">\n <div class=\"bg-white p-3 flex items-center justify-between rounded-md\">\n <div class=\"flex items-center\">\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" i18n-placeholder [(ngModel)]=\"searchOptions.town\">\n @for (Town of towns; track Town) {\n <mat-option [value]=\"Town\">\n {{ Town }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"flex gap-3\">\n <a mat-stroked-button class=\"w-[128px]\" (click)=\"reset()\" i18n>Reset</a>\n <ng-container *ngComponentOutlet=\"propertyFilterIconComponent | async\"></ng-container>\n <a mat-flat-button class=\"w-full\" (click)=\"search()\" i18n>Search</a>\n </div>\n </div>\n</div> -->\n<div\n [ngClass]=\"filter() ? 'transition-height duration-500 ease-in-out' : 'md:h-14 h-0 transition-height duration-500 ease-in-out'\"\n>\n <div\n class=\"min-w-[256px] md:min-w-[320px] h-full m-3 flex flex-row md:flex-col md:h-full items-center md:items-start shadow-inner shadow-light-400 md:shadow-none overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"w-full h-full\">\n <div class=\"flex flex-col md:flex-row h-in justify-center items-center gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"searchOptions.city\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\">\n {{ town }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"searchOptions.available\"\n (dateInput)=\"searchOptions.available = $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 w-full gap-3\">\n <a mat-stroked-button class=\"w-[128px]\" (click)=\"reset()\">Reset</a>\n <ng-container *ngComponentOutlet=\"propertyFilterIconComponent | async\"></ng-container>\n <a mat-flat-button class=\"w-full\" (click)=\"search()\">Search</a>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.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: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.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: i5.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: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i7.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i7.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i7.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }], animations: [
1084
- trigger('filter', [
1085
- state('collapsed', style({ height: '0px', minHeight: '0' })),
1086
- state('expanded', style({ height: '*' })),
1087
- transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
1088
- ]),
1089
- ] });
1070
+ var PropertyViewType;
1071
+ (function (PropertyViewType) {
1072
+ PropertyViewType[PropertyViewType["GRID"] = 0] = "GRID";
1073
+ PropertyViewType[PropertyViewType["LIST"] = 1] = "LIST";
1074
+ })(PropertyViewType || (PropertyViewType = {}));
1075
+ class PropertyUtil {
1076
+ viewEvent = new EventEmitter();
1077
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyUtil, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1078
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyUtil, providedIn: 'root' });
1090
1079
  }
1091
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyFilterComponent, decorators: [{
1092
- type: Component,
1093
- args: [{ selector: 'rolatech-property-filter', imports: [
1094
- CommonModule,
1095
- MatButtonModule,
1096
- MatFormFieldModule,
1097
- MatInputModule,
1098
- MatSelectModule,
1099
- MatOptionModule,
1100
- FormsModule,
1101
- MatDatepickerModule,
1102
- ], animations: [
1103
- trigger('filter', [
1104
- state('collapsed', style({ height: '0px', minHeight: '0' })),
1105
- state('expanded', style({ height: '*' })),
1106
- transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
1107
- ]),
1108
- ], template: "<!-- <div class=\"p-3\">\n <div class=\"bg-white p-3 flex items-center justify-between rounded-md\">\n <div class=\"flex items-center\">\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" i18n-placeholder [(ngModel)]=\"searchOptions.town\">\n @for (Town of towns; track Town) {\n <mat-option [value]=\"Town\">\n {{ Town }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"flex gap-3\">\n <a mat-stroked-button class=\"w-[128px]\" (click)=\"reset()\" i18n>Reset</a>\n <ng-container *ngComponentOutlet=\"propertyFilterIconComponent | async\"></ng-container>\n <a mat-flat-button class=\"w-full\" (click)=\"search()\" i18n>Search</a>\n </div>\n </div>\n</div> -->\n<div\n [ngClass]=\"filter() ? 'transition-height duration-500 ease-in-out' : 'md:h-14 h-0 transition-height duration-500 ease-in-out'\"\n>\n <div\n class=\"min-w-[256px] md:min-w-[320px] h-full m-3 flex flex-row md:flex-col md:h-full items-center md:items-start shadow-inner shadow-light-400 md:shadow-none overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"w-full h-full\">\n <div class=\"flex flex-col md:flex-row h-in justify-center items-center gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select #select=\"matSelect\" placeholder=\"Town\" [(ngModel)]=\"searchOptions.city\">\n @for (town of towns; track town) {\n <mat-option [value]=\"town\">\n {{ town }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <input\n matInput\n placeholder=\"Available\"\n [matDatepicker]=\"startDatePicker\"\n [min]=\"minDate\"\n (focus)=\"startDatePicker.open()\"\n name=\"available\"\n [(ngModel)]=\"searchOptions.available\"\n (dateInput)=\"searchOptions.available = $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 w-full gap-3\">\n <a mat-stroked-button class=\"w-[128px]\" (click)=\"reset()\">Reset</a>\n <ng-container *ngComponentOutlet=\"propertyFilterIconComponent | async\"></ng-container>\n <a mat-flat-button class=\"w-full\" (click)=\"search()\">Search</a>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: ["mat-form-field{width:100%}\n"] }]
1080
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyUtil, decorators: [{
1081
+ type: Injectable,
1082
+ args: [{
1083
+ providedIn: 'root',
1084
+ }]
1109
1085
  }] });
1110
1086
 
1111
1087
  class PropertyLayoutComponent extends BaseComponent {
1112
1088
  propertyService = inject(PropertyService);
1089
+ util = inject(PropertyUtil);
1090
+ open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
1091
+ model = {};
1092
+ gridView = true;
1093
+ searchBar = viewChild('searchBar', ...(ngDevMode ? [{ debugName: "searchBar" }] : []));
1113
1094
  selectIndex = 0;
1114
1095
  filter = false;
1096
+ propertyFilterIconComponent = defer(() => import('./rolatech-angular-property-property-filter-icon.component-_P3G8E-x.mjs').then((m) => m.PropertyFilterIconComponent));
1115
1097
  ngOnInit() {
1116
- this.findCategories();
1117
- }
1118
- findCategories() {
1119
- // this.categoryService.find({}, false).subscribe({
1120
- // next: (res: any) => {
1121
- // this.categories = res.data;
1122
- // },
1123
- // });
1124
- }
1125
- loadPropertyByCategoryIndex(index) {
1126
- // this.selectIndex = index;
1127
- // const id = this.categories[index].id;
1128
- // if (index === 0) {
1129
- // this.router.navigate([`./`]);
1130
- // // this.propertyService.find({}).subscribe({
1131
- // // next: res => {
1132
- // // }
1133
- // // })
1134
- // } else {
1135
- // this.router.navigate([`../categories/${id}`], {
1136
- // relativeTo: this.route,
1137
- // });
1138
- // }
1139
1098
  }
1140
- nextCategory() { }
1099
+ toggleSearch() {
1100
+ this.open.update(v => !v);
1101
+ }
1102
+ onCloseSearch() {
1103
+ // this.open.set(false);
1104
+ // this.router.navigate([], { queryParams: {}, queryParamsHandling: 'merge' });
1105
+ this.router.navigate(["/properties"]);
1106
+ }
1107
+ toggleView() {
1108
+ if (this.gridView) {
1109
+ this.gridView = false;
1110
+ this.util.viewEvent.emit(PropertyViewType.LIST);
1111
+ }
1112
+ else {
1113
+ this.gridView = true;
1114
+ this.util.viewEvent.emit(PropertyViewType.GRID);
1115
+ }
1116
+ }
1117
+ onSearch(event) {
1118
+ const qp = Object.fromEntries(Object.entries(event).filter(([_, v]) => v !== undefined && v !== null && !(Array.isArray(v) && v.length === 0) && v !== ''));
1119
+ this.router.navigate([], {
1120
+ queryParams: qp,
1121
+ queryParamsHandling: 'merge', // keep other params like sort, etc.
1122
+ });
1123
+ }
1124
+ searchByText(event) {
1125
+ this.router.navigate([], {
1126
+ queryParams: { q: event, page: 1, limit: 15 },
1127
+ queryParamsHandling: 'merge', // keep other params like sort, etc.
1128
+ });
1129
+ }
1130
+ onReset(event) {
1131
+ this.router.navigate([], {
1132
+ queryParams: {}, // empty
1133
+ // omit queryParamsHandling so it REPLACES (not merges) → clears everything
1134
+ });
1135
+ }
1141
1136
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyLayoutComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
1142
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.0", type: PropertyLayoutComponent, isStandalone: true, selector: "rolatech-property-layout", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col min-w-[320px] max-w-[1280px] m-auto\">\n <rolatech-property-filter [(filter)]=\"filter\"></rolatech-property-filter>\n <div class=\"flex justify-between items-center\">\n <div class=\"p-3 text-2xl font-medium\" i18n>Properties</div>\n <button mat-button class=\"md:invisible\" (click)=\"filter = !filter\">\n <mat-icon>tune</mat-icon>\n <span>Filter</span>\n </button>\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n </div>\n</rolatech-container>\n", styles: [".property-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.property-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i1$4.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: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: PropertyFilterComponent, selector: "rolatech-property-filter", inputs: ["filter"], outputs: ["filterChange"] }] });
1137
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.2.0", type: PropertyLayoutComponent, isStandalone: true, selector: "rolatech-property-layout", viewQueries: [{ propertyName: "searchBar", first: true, predicate: ["searchBar"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col min-w-[320px] max-w-[1280px] m-auto\">\n <div class=\"flex justify-between items-center h-16\">\n <div class=\"flex justify-between items-center w-full\">\n <div class=\"p-3 text-2xl font-medium\" i18n>Properties</div>\n <div class=\"flex flex-row\">\n <button mat-icon-button (click)=\"toggleSearch()\">\n <mat-icon>search</mat-icon>\n </button>\n <button mat-icon-button (click)=\"toggleView()\">\n <mat-icon>{{ gridView ? 'view_list' : 'grid_on' }}</mat-icon>\n </button>\n <ng-container *ngComponentOutlet=\"propertyFilterIconComponent | async\"></ng-container>\n </div>\n </div>\n </div>\n <rolatech-search-bar\n [(show)]=\"open\"\n #searchBar\n (search)=\"searchByText($event)\"\n (close)=\"onCloseSearch()\"\n ></rolatech-search-bar>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n </div>\n</rolatech-container>\n", styles: [".property-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.property-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: i1$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i1$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: SearchBar, selector: "rolatech-search-bar", inputs: ["show"], outputs: ["showChange", "search", "close"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }] });
1143
1138
  }
1144
1139
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyLayoutComponent, decorators: [{
1145
1140
  type: Component,
1146
- args: [{ selector: 'rolatech-property-layout', imports: [AngularCommonModule, AngularComponentsModule, ContainerComponent, PropertyFilterComponent], template: "<rolatech-container>\n <div class=\"flex flex-col min-w-[320px] max-w-[1280px] m-auto\">\n <rolatech-property-filter [(filter)]=\"filter\"></rolatech-property-filter>\n <div class=\"flex justify-between items-center\">\n <div class=\"p-3 text-2xl font-medium\" i18n>Properties</div>\n <button mat-button class=\"md:invisible\" (click)=\"filter = !filter\">\n <mat-icon>tune</mat-icon>\n <span>Filter</span>\n </button>\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n </div>\n</rolatech-container>\n", styles: [".property-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.property-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
1141
+ args: [{ selector: 'rolatech-property-layout', imports: [AngularCommonModule, AngularComponentsModule, ContainerComponent, SearchBar], template: "<rolatech-container>\n <div class=\"flex flex-col min-w-[320px] max-w-[1280px] m-auto\">\n <div class=\"flex justify-between items-center h-16\">\n <div class=\"flex justify-between items-center w-full\">\n <div class=\"p-3 text-2xl font-medium\" i18n>Properties</div>\n <div class=\"flex flex-row\">\n <button mat-icon-button (click)=\"toggleSearch()\">\n <mat-icon>search</mat-icon>\n </button>\n <button mat-icon-button (click)=\"toggleView()\">\n <mat-icon>{{ gridView ? 'view_list' : 'grid_on' }}</mat-icon>\n </button>\n <ng-container *ngComponentOutlet=\"propertyFilterIconComponent | async\"></ng-container>\n </div>\n </div>\n </div>\n <rolatech-search-bar\n [(show)]=\"open\"\n #searchBar\n (search)=\"searchByText($event)\"\n (close)=\"onCloseSearch()\"\n ></rolatech-search-bar>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n </div>\n</rolatech-container>\n", styles: [".property-layout-active{border-radius:var(--rt-rounded-base, 8px);background-color:var(-rt-base-background, #fff);color:var(--rt-brand-color, #000)}@media (max-width: 768px){.property-layout-active{background-color:var(--rt-text-primary, #000);color:var(--rt-text-primary-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
1147
1142
  }] });
1148
1143
 
1149
1144
  class PropertyActionsComponent {
@@ -1240,22 +1235,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImpor
1240
1235
  args: [{ selector: 'rolatech-property-section', imports: [CommonModule, ThumbnailComponent], encapsulation: ViewEncapsulation.None, template: "@if (section(); as section) {\n <div>\n @if (section.title) {\n <div class=\"py-3 flex items-center gap-3\">\n <span class=\"h-4 w-1 bg-[--rt-brand-color] inline-block\"></span>\n <span class=\"text-lg font-medium py-1\"> {{ section.title }}</span>\n </div>\n }\n @if (section.description) {\n <div>\n {{ section.description }}\n </div>\n }\n @if (section.media) {\n <div class=\"w-80%\">\n @for (item of section.media; track item) {\n <div class=\"py-3\">\n <rolatech-thumbnail\n [src]=\"item.url\"\n size=\"small\"\n mode=\"full\"\n [width]=\"item.width\"\n [height]=\"item.height\"\n ></rolatech-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n}\n" }]
1241
1236
  }] });
1242
1237
 
1243
- class PropertyVideoTourComponent {
1238
+ class PropertyVideoComponent {
1244
1239
  sanitizer = inject(DomSanitizer);
1245
- videoTour = input.required(...(ngDevMode ? [{ debugName: "videoTour" }] : []));
1240
+ video = input.required(...(ngDevMode ? [{ debugName: "video" }] : []));
1246
1241
  videoUrl = computed(() => {
1247
- const match = this.videoTour().url.match(/(?:youtu\.be\/|youtube\.com\/watch\?v=)([\w-]+)/);
1242
+ const match = this.video().url.match(/(?:youtu\.be\/|youtube\.com\/watch\?v=)([\w-]+)/);
1248
1243
  if (match && match[1]) {
1249
1244
  return this.sanitizer.bypassSecurityTrustResourceUrl(`https://www.youtube.com/embed/${match[1]}`);
1250
1245
  }
1251
1246
  return null; // Not a valid YouTube URL
1252
1247
  }, ...(ngDevMode ? [{ debugName: "videoUrl" }] : []));
1253
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoTourComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1254
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.0", type: PropertyVideoTourComponent, isStandalone: true, selector: "rolatech-property-video-tour", inputs: { videoTour: { classPropertyName: "videoTour", publicName: "videoTour", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"\">\n <div class=\"text-2xl font-bold py-4\" i18n>Video tour</div>\n <iframe [src]=\"videoUrl()\" frameborder=\"0\" allowfullscreen class=\"w-full aspect-video\"></iframe>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1248
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1249
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.0", type: PropertyVideoComponent, isStandalone: true, selector: "rolatech-property-video", inputs: { video: { classPropertyName: "video", publicName: "video", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"\">\n <div class=\"text-2xl font-bold py-4\" i18n>Videos</div>\n <iframe [src]=\"videoUrl()\" frameborder=\"0\" allowfullscreen class=\"w-full aspect-video\"></iframe>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1255
1250
  }
1256
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoTourComponent, decorators: [{
1251
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoComponent, decorators: [{
1257
1252
  type: Component,
1258
- args: [{ selector: 'rolatech-property-video-tour', imports: [CommonModule], template: "<div class=\"\">\n <div class=\"text-2xl font-bold py-4\" i18n>Video tour</div>\n <iframe [src]=\"videoUrl()\" frameborder=\"0\" allowfullscreen class=\"w-full aspect-video\"></iframe>\n</div>\n" }]
1253
+ args: [{ selector: 'rolatech-property-video', imports: [CommonModule], template: "<div class=\"\">\n <div class=\"text-2xl font-bold py-4\" i18n>Videos</div>\n <iframe [src]=\"videoUrl()\" frameborder=\"0\" allowfullscreen class=\"w-full aspect-video\"></iframe>\n</div>\n" }]
1259
1254
  }] });
1260
1255
 
1261
1256
  class PropertyActionContactComponent {
@@ -1289,6 +1284,153 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImpor
1289
1284
  args: [{ selector: 'rolatech-property-agent-renderer', imports: [AngularCommonModule, MatIconModule], template: "<div class=\"flex flex-col\">\n <div class=\"flex flex-row items-center py-3\">\n <div class=\"flex mr-3 gap-2 items-center\">\n @if (avatar()) {\n <div class=\"cursor-pointer\" [routerLink]=\"['/', '@' + username()]\">\n <img [src]=\"avatar()\" class=\"w-14 h-14 rounded-full\" alt />\n </div>\n } @else {\n <div class=\"w-14 h-14 rounded-full bg-[--rt-brand-color]\"></div>\n }\n <div class=\"flex flex-col\">\n <div class=\"flex items-center text-lg font-bold cursor-pointer\">\n <a [routerLink]=\"['/', '@' + username()]\">\n <span>{{ name() }}</span>\n </a>\n <mat-icon class=\"verified-icon\">verified</mat-icon>\n </div>\n <div class=\"text-lg font-bold text-[--rt-brand-color]\">{{ subtitle() }}</div>\n </div>\n </div>\n </div>\n <div class=\"text-sm opacity-70\">This listing is verified and managed by an Primecase agent</div>\n</div>\n" }]
1290
1285
  }] });
1291
1286
 
1287
+ class PropertyHighlights {
1288
+ highlights = input.required(...(ngDevMode ? [{ debugName: "highlights" }] : []));
1289
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyHighlights, deps: [], target: i0.ɵɵFactoryTarget.Component });
1290
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyHighlights, isStandalone: true, selector: "rolatech-property-highlights", inputs: { highlights: { classPropertyName: "highlights", publicName: "highlights", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"\">\n <div class=\"text-2xl font-bold py-3\" i18n>Highlights</div>\n <div class=\"flex flex-col flex-wrap\">\n @for (item of highlights(); track $index) {\n <div class=\"flex items-center py-1\">\n <!-- <mat-icon>check</mat-icon> -->\n <span>{{ item }}</span>\n </div>\n }\n </div>\n</div>\n", styles: [""] });
1291
+ }
1292
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyHighlights, decorators: [{
1293
+ type: Component,
1294
+ args: [{ selector: 'rolatech-property-highlights', template: "<div class=\"\">\n <div class=\"text-2xl font-bold py-3\" i18n>Highlights</div>\n <div class=\"flex flex-col flex-wrap\">\n @for (item of highlights(); track $index) {\n <div class=\"flex items-center py-1\">\n <!-- <mat-icon>check</mat-icon> -->\n <span>{{ item }}</span>\n </div>\n }\n </div>\n</div>\n" }]
1295
+ }] });
1296
+
1297
+ const BANDS = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
1298
+ const RANGES = {
1299
+ A: '(92+)', B: '(81-91)', C: '(69-80)', D: '(55-68)',
1300
+ E: '(39-54)', F: '(21-38)', G: '(1-20)',
1301
+ };
1302
+ const COLORS = {
1303
+ A: '#0E9F6E', // green
1304
+ B: '#10B981',
1305
+ C: '#84CC16',
1306
+ D: '#F59E0B',
1307
+ E: '#F97316',
1308
+ F: '#FB7185',
1309
+ G: '#DC2626'
1310
+ };
1311
+ function bandFromScore(s) {
1312
+ const v = s ?? 0;
1313
+ if (v >= 92)
1314
+ return 'A';
1315
+ if (v >= 81)
1316
+ return 'B';
1317
+ if (v >= 69)
1318
+ return 'C';
1319
+ if (v >= 55)
1320
+ return 'D';
1321
+ if (v >= 39)
1322
+ return 'E';
1323
+ if (v >= 21)
1324
+ return 'F';
1325
+ return 'G';
1326
+ }
1327
+ class EpcChart {
1328
+ // --- inputs (signals) ---
1329
+ currentScore = input(null, ...(ngDevMode ? [{ debugName: "currentScore" }] : [])); // 1..100
1330
+ potentialScore = input(null, ...(ngDevMode ? [{ debugName: "potentialScore" }] : [])); // 1..100
1331
+ showCaptions = input(true, ...(ngDevMode ? [{ debugName: "showCaptions" }] : []));
1332
+ // --- layout ---
1333
+ width = 920;
1334
+ leftPadding = 24;
1335
+ headerY = 40;
1336
+ rangeColW = 90;
1337
+ gapY = 10;
1338
+ topY = 110; // top of first row area
1339
+ rowH = 48; // height of each wedge row
1340
+ rows = 7;
1341
+ // wedge geometry
1342
+ wedgeMaxW = 420; // width of 'A' row
1343
+ wedgeMinW = 220;
1344
+ wedgeDelta = 44; // decrement per row
1345
+ cut = 24; // right slant depth
1346
+ // arrow columns
1347
+ arrowW = 104;
1348
+ arrowH = 44;
1349
+ colGap = 130;
1350
+ wedgeAreaRight = this.leftPadding + this.rangeColW + 12 + this.wedgeMaxW + 20;
1351
+ currentColX = this.wedgeAreaRight + 80;
1352
+ potentialColX = this.currentColX + this.colGap;
1353
+ // captions
1354
+ topCaptionY = this.topY - 22;
1355
+ get bottomCaptionY() { return this.topY + this.rows * (this.rowH + this.gapY) + 22; }
1356
+ // overall height
1357
+ get height() {
1358
+ return this.bottomCaptionY + 56;
1359
+ }
1360
+ // expose constants to template
1361
+ bands = BANDS;
1362
+ RANGES = RANGES;
1363
+ COLORS = COLORS;
1364
+ get rangeX() { return this.leftPadding; }
1365
+ get maxW() { return this.wedgeMinW + 6 * this.wedgeDelta; } // width of G (widest)
1366
+ // helpers
1367
+ wedgeX(i) { return this.leftPadding + this.rangeColW + 12; }
1368
+ // private wedgeW(i: number) { return this.wedgeMaxW - (i * this.wedgeDelta); }
1369
+ wedgeW(i) {
1370
+ // A..G => 0..6 → widths increase downward
1371
+ return this.wedgeMinW + i * this.wedgeDelta;
1372
+ }
1373
+ rowY(i) { return this.topY + i * (this.rowH + this.gapY); }
1374
+ wedgePoints1(i) {
1375
+ const x = this.wedgeX(i), y = this.rowY(i), w = this.wedgeW(i), h = this.rowH, c = this.cut;
1376
+ // rectangle with right chevron tip
1377
+ // (x,y) → (x+w-c,y) → (x+w,y+h/2) → (x+w-c,y+h) → (x,y+h)
1378
+ return `${x},${y} ${x + w - c},${y} ${x + w},${y + h / 2} ${x + w - c},${y + h} ${x},${y + h}`;
1379
+ }
1380
+ wedgePoints(i) {
1381
+ const x = this.wedgeX(i), y = this.rowY(i), w = this.wedgeW(i), h = this.rowH;
1382
+ // rectangle: (x,y) → (x+w,y) → (x+w,y+h) → (x,y+h)
1383
+ return `${x},${y} ${x + w},${y} ${x + w},${y + h} ${x},${y + h}`;
1384
+ }
1385
+ wedgeLetterX(i) {
1386
+ // place on the trailing (right) end of wedge, just before chevron tip
1387
+ return this.wedgeX(i) + this.wedgeW(i) - this.cut - 10;
1388
+ }
1389
+ // arrow badge (left-pointing)
1390
+ arrowPoints() {
1391
+ const w = this.arrowW, h = this.arrowH, tip = 18; // tip depth
1392
+ // (w,0) → (tip,0) → (0,h/2) → (tip,h) → (w,h) → close
1393
+ return `${w},0 ${tip},0 0,${h / 2} ${tip},${h} ${w},${h}`;
1394
+ }
1395
+ // vertical placement: center the arrow in the band row for the given score
1396
+ arrowY(score) {
1397
+ const b = bandFromScore(score);
1398
+ const rowIndex = this.bands.indexOf(b);
1399
+ const y = this.rowY(rowIndex) + (this.rowH - this.arrowH) / 2;
1400
+ return Math.max(y, this.topY); // safety
1401
+ }
1402
+ clampScore(s) { return Math.max(1, Math.min(100, Math.round(s))); }
1403
+ // add below COLORS, bandFromScore...
1404
+ colorForBand(b) { return COLORS[b]; }
1405
+ colorForScore(s) { return this.colorForBand(bandFromScore(s)); }
1406
+ // simple contrast so numbers stay readable
1407
+ textColorForBand(b) {
1408
+ const hex = this.colorForBand(b).replace('#', '');
1409
+ const r = parseInt(hex.slice(0, 2), 16);
1410
+ const g = parseInt(hex.slice(2, 4), 16);
1411
+ const bl = parseInt(hex.slice(4, 6), 16);
1412
+ const yiq = (r * 299 + g * 587 + bl * 114) / 1000;
1413
+ return yiq >= 170 ? '#111' : '#fff'; // light bg → dark text, else white
1414
+ }
1415
+ textColorForScore(s) { return this.textColorForBand(bandFromScore(s)); }
1416
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: EpcChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
1417
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: EpcChart, isStandalone: true, selector: "rolatech-epc-chart", inputs: { currentScore: { classPropertyName: "currentScore", publicName: "currentScore", isSignal: true, isRequired: false, transformFunction: null }, potentialScore: { classPropertyName: "potentialScore", publicName: "potentialScore", isSignal: true, isRequired: false, transformFunction: null }, showCaptions: { classPropertyName: "showCaptions", publicName: "showCaptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "w-full" }, ngImport: i0, template: "<svg [attr.viewBox]=\"'0 0 ' + width + ' ' + height\" class=\"w-full h-auto\">\n <!-- outer border -->\n <rect x=\"8\" y=\"8\" [attr.width]=\"width-16\" [attr.height]=\"height-16\" fill=\"none\" stroke=\"#111\" stroke-width=\"2\" />\n\n <!-- header row -->\n <text [attr.x]=\"leftPadding\" [attr.y]=\"headerY\" font-weight=\"700\" font-size=\"16\">Energy Rating</text>\n <text [attr.x]=\"currentColX + arrowW/2\" [attr.y]=\"headerY\" text-anchor=\"middle\" font-weight=\"700\" font-size=\"16\">\n Current\n </text>\n <text [attr.x]=\"potentialColX + arrowW/2\" [attr.y]=\"headerY\" text-anchor=\"middle\" font-weight=\"700\" font-size=\"16\">\n Potential\n </text>\n <line x1=\"8\" [attr.y1]=\"headerY+10\" [attr.x2]=\"width-8\" [attr.y2]=\"headerY+10\" stroke=\"#111\" stroke-width=\"2\" />\n\n <!-- top/bottom captions -->\n @if (showCaptions()) {\n <text [attr.x]=\"leftPadding\" [attr.y]=\"topCaptionY\" fill=\"#9CA3AF\" font-size=\"20\">\n very energy efficient - lower running costs\n </text>\n <text [attr.x]=\"leftPadding\" [attr.y]=\"bottomCaptionY\" fill=\"#9CA3AF\" font-size=\"20\">\n not energy efficient - higher running costs\n </text>\n }\n\n <!-- Wedges (A..G) -->\n @for (b of bands; track b; let i = $index) {\n <!-- range text column -->\n <text [attr.x]=\"rangeX\" [attr.y]=\"rowY(i) + rowH/2 + 5\" fill=\"#6B7280\" font-size=\"16\">{{ RANGES[b] }}</text>\n\n <!-- colored wedge -->\n <polygon [attr.points]=\"wedgePoints(i)\" [attr.fill]=\"COLORS[b]\" stroke=\"none\" />\n\n <!-- band letter -->\n <text\n [attr.x]=\"wedgeLetterX(i)\"\n [attr.y]=\"rowY(i) + rowH/2 + 8\"\n fill=\"white\"\n font-size=\"28\"\n font-weight=\"700\"\n text-anchor=\"end\"\n >\n {{ b }}\n </text>\n }\n\n <!-- Current arrow badge -->\n @if (currentScore() !== null) {\n <g [attr.transform]=\"'translate(' + currentColX + ',' + arrowY(currentScore()!) + ')'\">\n <polygon [attr.points]=\"arrowPoints()\" [attr.fill]=\"colorForScore(currentScore()!)\"></polygon>\n <text\n [attr.x]=\"arrowW/2 + 6\"\n [attr.y]=\"arrowH/2 + 9\"\n text-anchor=\"middle\"\n font-size=\"24\"\n font-weight=\"800\"\n [attr.fill]=\"textColorForScore(currentScore()!)\"\n >\n {{ clampScore(currentScore()!) }}\n </text>\n <!-- underline (optional) -->\n <rect\n [attr.x]=\"arrowW/2 - 12\"\n [attr.y]=\"arrowH/2 + 12\"\n width=\"36\"\n height=\"3\"\n [attr.fill]=\"textColorForScore(currentScore()!)\"\n opacity=\"0.8\"\n />\n </g>\n }\n\n <!-- Potential arrow badge -->\n @if (potentialScore() !== null) {\n <g [attr.transform]=\"'translate(' + potentialColX + ',' + arrowY(potentialScore()!) + ')'\">\n <polygon [attr.points]=\"arrowPoints()\" [attr.fill]=\"colorForScore(potentialScore()!)\"></polygon>\n <text\n [attr.x]=\"arrowW/2 + 6\"\n [attr.y]=\"arrowH/2 + 9\"\n text-anchor=\"middle\"\n font-size=\"24\"\n font-weight=\"800\"\n [attr.fill]=\"textColorForScore(potentialScore()!)\"\n >\n {{ clampScore(potentialScore()!) }}\n </text>\n </g>\n }\n</svg>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
1418
+ }
1419
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: EpcChart, decorators: [{
1420
+ type: Component,
1421
+ args: [{ selector: 'rolatech-epc-chart', imports: [CommonModule], host: { class: 'w-full' }, template: "<svg [attr.viewBox]=\"'0 0 ' + width + ' ' + height\" class=\"w-full h-auto\">\n <!-- outer border -->\n <rect x=\"8\" y=\"8\" [attr.width]=\"width-16\" [attr.height]=\"height-16\" fill=\"none\" stroke=\"#111\" stroke-width=\"2\" />\n\n <!-- header row -->\n <text [attr.x]=\"leftPadding\" [attr.y]=\"headerY\" font-weight=\"700\" font-size=\"16\">Energy Rating</text>\n <text [attr.x]=\"currentColX + arrowW/2\" [attr.y]=\"headerY\" text-anchor=\"middle\" font-weight=\"700\" font-size=\"16\">\n Current\n </text>\n <text [attr.x]=\"potentialColX + arrowW/2\" [attr.y]=\"headerY\" text-anchor=\"middle\" font-weight=\"700\" font-size=\"16\">\n Potential\n </text>\n <line x1=\"8\" [attr.y1]=\"headerY+10\" [attr.x2]=\"width-8\" [attr.y2]=\"headerY+10\" stroke=\"#111\" stroke-width=\"2\" />\n\n <!-- top/bottom captions -->\n @if (showCaptions()) {\n <text [attr.x]=\"leftPadding\" [attr.y]=\"topCaptionY\" fill=\"#9CA3AF\" font-size=\"20\">\n very energy efficient - lower running costs\n </text>\n <text [attr.x]=\"leftPadding\" [attr.y]=\"bottomCaptionY\" fill=\"#9CA3AF\" font-size=\"20\">\n not energy efficient - higher running costs\n </text>\n }\n\n <!-- Wedges (A..G) -->\n @for (b of bands; track b; let i = $index) {\n <!-- range text column -->\n <text [attr.x]=\"rangeX\" [attr.y]=\"rowY(i) + rowH/2 + 5\" fill=\"#6B7280\" font-size=\"16\">{{ RANGES[b] }}</text>\n\n <!-- colored wedge -->\n <polygon [attr.points]=\"wedgePoints(i)\" [attr.fill]=\"COLORS[b]\" stroke=\"none\" />\n\n <!-- band letter -->\n <text\n [attr.x]=\"wedgeLetterX(i)\"\n [attr.y]=\"rowY(i) + rowH/2 + 8\"\n fill=\"white\"\n font-size=\"28\"\n font-weight=\"700\"\n text-anchor=\"end\"\n >\n {{ b }}\n </text>\n }\n\n <!-- Current arrow badge -->\n @if (currentScore() !== null) {\n <g [attr.transform]=\"'translate(' + currentColX + ',' + arrowY(currentScore()!) + ')'\">\n <polygon [attr.points]=\"arrowPoints()\" [attr.fill]=\"colorForScore(currentScore()!)\"></polygon>\n <text\n [attr.x]=\"arrowW/2 + 6\"\n [attr.y]=\"arrowH/2 + 9\"\n text-anchor=\"middle\"\n font-size=\"24\"\n font-weight=\"800\"\n [attr.fill]=\"textColorForScore(currentScore()!)\"\n >\n {{ clampScore(currentScore()!) }}\n </text>\n <!-- underline (optional) -->\n <rect\n [attr.x]=\"arrowW/2 - 12\"\n [attr.y]=\"arrowH/2 + 12\"\n width=\"36\"\n height=\"3\"\n [attr.fill]=\"textColorForScore(currentScore()!)\"\n opacity=\"0.8\"\n />\n </g>\n }\n\n <!-- Potential arrow badge -->\n @if (potentialScore() !== null) {\n <g [attr.transform]=\"'translate(' + potentialColX + ',' + arrowY(potentialScore()!) + ')'\">\n <polygon [attr.points]=\"arrowPoints()\" [attr.fill]=\"colorForScore(potentialScore()!)\"></polygon>\n <text\n [attr.x]=\"arrowW/2 + 6\"\n [attr.y]=\"arrowH/2 + 9\"\n text-anchor=\"middle\"\n font-size=\"24\"\n font-weight=\"800\"\n [attr.fill]=\"textColorForScore(potentialScore()!)\"\n >\n {{ clampScore(potentialScore()!) }}\n </text>\n </g>\n }\n</svg>\n" }]
1422
+ }] });
1423
+
1424
+ class PropertyEpc {
1425
+ epc = input.required(...(ngDevMode ? [{ debugName: "epc" }] : []));
1426
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyEpc, deps: [], target: i0.ɵɵFactoryTarget.Component });
1427
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.2.0", type: PropertyEpc, isStandalone: true, selector: "rolatech-property-epc", inputs: { epc: { classPropertyName: "epc", publicName: "epc", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"w-full\">\n <div class=\"text-2xl font-bold py-3\" i18n>EPC</div>\n <div class=\"flex flex-row items-center font-thin text-sm\">\n <rolatech-epc-chart\n [currentScore]=\"epc().currentScore\"\n [potentialScore]=\"epc().potentialScore\"\n [showCaptions]=\"true\"\n ></rolatech-epc-chart>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: EpcChart, selector: "rolatech-epc-chart", inputs: ["currentScore", "potentialScore", "showCaptions"] }] });
1428
+ }
1429
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyEpc, decorators: [{
1430
+ type: Component,
1431
+ args: [{ selector: 'rolatech-property-epc', imports: [EpcChart], template: "<div class=\"w-full\">\n <div class=\"text-2xl font-bold py-3\" i18n>EPC</div>\n <div class=\"flex flex-row items-center font-thin text-sm\">\n <rolatech-epc-chart\n [currentScore]=\"epc().currentScore\"\n [potentialScore]=\"epc().potentialScore\"\n [showCaptions]=\"true\"\n ></rolatech-epc-chart>\n </div>\n</div>\n" }]
1432
+ }] });
1433
+
1292
1434
  class PropertyDetailsComponent extends BaseComponent {
1293
1435
  authService = inject(AuthService);
1294
1436
  authUserService = inject(AuthUserService);
@@ -1303,6 +1445,8 @@ class PropertyDetailsComponent extends BaseComponent {
1303
1445
  selectedOption;
1304
1446
  variants = [];
1305
1447
  features = [];
1448
+ highlights = [];
1449
+ epc;
1306
1450
  selectedVariant;
1307
1451
  variantOption = signal(undefined, ...(ngDevMode ? [{ debugName: "variantOption" }] : []));
1308
1452
  variantOptionChanged = computed(() => {
@@ -1320,6 +1464,8 @@ class PropertyDetailsComponent extends BaseComponent {
1320
1464
  ngOnInit() {
1321
1465
  this.getProperty();
1322
1466
  this.findFeatures();
1467
+ this.findHighlights();
1468
+ this.getEpc();
1323
1469
  }
1324
1470
  getProperty() {
1325
1471
  this.propertyService.get(this.id).subscribe({
@@ -1356,10 +1502,23 @@ class PropertyDetailsComponent extends BaseComponent {
1356
1502
  });
1357
1503
  }
1358
1504
  findFeatures() {
1359
- console.log(this.id);
1360
1505
  this.propertyService.findFeatures(this.id).subscribe({
1361
1506
  next: (res) => {
1362
- this.features = res.data;
1507
+ this.features = res.data || [];
1508
+ }
1509
+ });
1510
+ }
1511
+ findHighlights() {
1512
+ this.propertyService.getHighlights(this.id).subscribe({
1513
+ next: (res) => {
1514
+ this.highlights = res.data || [];
1515
+ }
1516
+ });
1517
+ }
1518
+ getEpc() {
1519
+ this.propertyService.getEpc(this.id).subscribe({
1520
+ next: (res) => {
1521
+ this.epc = res.data;
1363
1522
  }
1364
1523
  });
1365
1524
  }
@@ -1441,7 +1600,7 @@ class PropertyDetailsComponent extends BaseComponent {
1441
1600
  // const updatedCriteria = matchingVariant ? updateCriteriaFromVariant(matchingVariant) : [];
1442
1601
  }
1443
1602
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1444
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyDetailsComponent, isStandalone: true, selector: "rolatech-property-details", usesInheritance: true, ngImport: i0, template: "@if (property) {\n <rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <mat-divider></mat-divider>\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videoTours) {\n @for (item of property.videoTours; track $index) {\n <rolatech-property-video-tour [videoTour]=\"item\"></rolatech-property-video-tour>\n }\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 </div>\n </rolatech-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i2$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: PropertyInfoComponent, selector: "rolatech-property-info", inputs: ["property", "inWishList"], outputs: ["wish"] }, { kind: "component", type: PropertyMediaComponent, selector: "rolatech-property-media", inputs: ["media", "min"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertySectionComponent, selector: "rolatech-property-section", inputs: ["section", "user", "username"] }, { kind: "component", type: PropertyActionsComponent, selector: "rolatech-property-actions", inputs: ["property"], outputs: ["requestViewing", "offer", "deposit"] }, { kind: "component", type: CommentsComponent, selector: "rolatech-comments", inputs: ["itemId"] }, { kind: "component", type: PropertyAgentRenderer, selector: "rolatech-property-agent-renderer", inputs: ["name", "avatar", "username", "subtitle"] }, { kind: "component", type: PropertyLocationComponent, selector: "rolatech-property-location", inputs: ["location"] }, { kind: "component", type: PropertyFeaturesComponent, selector: "rolatech-property-features", inputs: ["features"] }, { kind: "component", type: PropertyVideoTourComponent, selector: "rolatech-property-video-tour", inputs: ["videoTour"] }, { kind: "component", type: PropertyActionContactComponent, selector: "rolatech-property-action-contact", inputs: ["email", "phone"], outputs: ["callAgent", "emailAgent"] }] });
1603
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyDetailsComponent, isStandalone: true, selector: "rolatech-property-details", usesInheritance: true, ngImport: i0, template: "@if (property) {\n <rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <mat-divider></mat-divider>\n\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 <!-- <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc> -->\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videos) {\n @for (item of property.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 </div>\n </rolatech-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i2$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: PropertyInfoComponent, selector: "rolatech-property-info", inputs: ["property", "inWishList"], outputs: ["wish"] }, { kind: "component", type: PropertyMediaComponent, selector: "rolatech-property-media", inputs: ["media", "min"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertySectionComponent, selector: "rolatech-property-section", inputs: ["section", "user", "username"] }, { kind: "component", type: PropertyActionsComponent, selector: "rolatech-property-actions", inputs: ["property"], outputs: ["requestViewing", "offer", "deposit"] }, { kind: "component", type: CommentsComponent, selector: "rolatech-comments", inputs: ["itemId"] }, { kind: "component", type: PropertyAgentRenderer, selector: "rolatech-property-agent-renderer", inputs: ["name", "avatar", "username", "subtitle"] }, { kind: "component", type: PropertyLocationComponent, selector: "rolatech-property-location", inputs: ["location"] }, { kind: "component", type: PropertyFeaturesComponent, selector: "rolatech-property-features", inputs: ["features"] }, { kind: "component", type: PropertyVideoComponent, selector: "rolatech-property-video", inputs: ["video"] }, { kind: "component", type: PropertyActionContactComponent, selector: "rolatech-property-action-contact", inputs: ["email", "phone"], outputs: ["callAgent", "emailAgent"] }, { kind: "component", type: PropertyHighlights, selector: "rolatech-property-highlights", inputs: ["highlights"] }, { kind: "component", type: PropertyEpc, selector: "rolatech-property-epc", inputs: ["epc"] }] });
1445
1604
  }
1446
1605
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyDetailsComponent, decorators: [{
1447
1606
  type: Component,
@@ -1458,9 +1617,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImpor
1458
1617
  PropertyAgentRenderer,
1459
1618
  PropertyLocationComponent,
1460
1619
  PropertyFeaturesComponent,
1461
- PropertyVideoTourComponent,
1620
+ PropertyVideoComponent,
1462
1621
  PropertyActionContactComponent,
1463
- ], template: "@if (property) {\n <rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <mat-divider></mat-divider>\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videoTours) {\n @for (item of property.videoTours; track $index) {\n <rolatech-property-video-tour [videoTour]=\"item\"></rolatech-property-video-tour>\n }\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 </div>\n </rolatech-container>\n}\n" }]
1622
+ PropertyHighlights,
1623
+ PropertyEpc,
1624
+ ], template: "@if (property) {\n <rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <mat-divider></mat-divider>\n\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 <!-- <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc> -->\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videos) {\n @for (item of property.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 </div>\n </rolatech-container>\n}\n" }]
1464
1625
  }], ctorParameters: () => [] });
1465
1626
 
1466
1627
  const MY_FORMATS$5 = {
@@ -2270,7 +2431,7 @@ const propertyRoutes = [
2270
2431
  children: [
2271
2432
  {
2272
2433
  path: '',
2273
- loadComponent: () => import('./rolatech-angular-property-property-index.component-Birv1xyB.mjs').then((x) => x.PropertyIndexComponent),
2434
+ loadComponent: () => import('./rolatech-angular-property-property-index.component-CbbxIQ8w.mjs').then((x) => x.PropertyIndexComponent),
2274
2435
  },
2275
2436
  ],
2276
2437
  },
@@ -2339,6 +2500,7 @@ class PropertyManageIndexComponent {
2339
2500
  dialog = inject(MatDialog);
2340
2501
  snackBar = inject(MatSnackBar);
2341
2502
  propertyService = inject(PropertyService);
2503
+ propertySearchService = inject(PropertySearchService);
2342
2504
  isLoading = false;
2343
2505
  isSearch = false;
2344
2506
  properties = [];
@@ -2492,11 +2654,11 @@ class PropertyManageLayoutComponent extends BaseComponent {
2492
2654
  });
2493
2655
  }
2494
2656
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageLayoutComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2495
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyManageLayoutComponent, isStandalone: true, selector: "rolatech-property-manage-layout", usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 md:px-0 flex flex-row md:flex-col md:h-full items-center md:items-start h-16 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-1\" i18n>Property</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Basic info</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Media</a>\n <a routerLink=\"./video-tour\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Video tour</a>\n <a routerLink=\"./sections\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Details</a>\n <a routerLink=\"./location\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Location</a>\n <a routerLink=\"./features\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Features</a>\n <!-- <a routerLink=\"./amenities\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Amenities</a> -->\n <!-- <a routerLink=\"./tags\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Tags</a> -->\n <!-- <a routerLink=\"./options\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Options</a> -->\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Settings</div>\n <!-- <a routerLink=\"./variants\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Variants</a> -->\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Schedule</a>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Pricing</a>\n </div>\n <!-- <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Others</div>\n </div> -->\n @if (property) {\n @if (property.status.toString() === 'DRAFT') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"submit()\" i18n>Submit for review</button>\n </div>\n }\n @if (property.status.toString() === 'PENDING') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button disabled i18n>Pending</button>\n </div>\n }\n @if (property.status.toString() === 'APPROVED') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"publish()\" i18n>Publish</button>\n </div>\n }\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.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: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], encapsulation: i0.ViewEncapsulation.None });
2657
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyManageLayoutComponent, isStandalone: true, selector: "rolatech-property-manage-layout", usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 md:px-0 flex flex-row md:flex-col md:h-full items-center md:items-start h-16 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-1\" i18n>Property</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Basic info</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Media</a>\n <a routerLink=\"./videos\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Videos</a>\n <a routerLink=\"./sections\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Details</a>\n <a routerLink=\"./location\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Location</a>\n <a routerLink=\"./features\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Features</a>\n <a routerLink=\"./highlights\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Highlights</a>\n <a routerLink=\"./epc\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>EPC</a>\n <a routerLink=\"./translation\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Translation</a>\n <!-- <a routerLink=\"./amenities\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Amenities</a> -->\n <!-- <a routerLink=\"./tags\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Tags</a> -->\n <!-- <a routerLink=\"./options\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Options</a> -->\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Settings</div>\n <!-- <a routerLink=\"./variants\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Variants</a> -->\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Schedule</a>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Pricing</a>\n </div>\n <!-- <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Others</div>\n </div> -->\n @if (property) {\n @if (property.status.toString() === 'DRAFT') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"submit()\" i18n>Submit for review</button>\n </div>\n }\n @if (property.status.toString() === 'PENDING') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button disabled i18n>Pending</button>\n </div>\n }\n @if (property.status.toString() === 'APPROVED') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"publish()\" i18n>Publish</button>\n </div>\n }\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.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: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], encapsulation: i0.ViewEncapsulation.None });
2496
2658
  }
2497
2659
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageLayoutComponent, decorators: [{
2498
2660
  type: Component,
2499
- args: [{ selector: 'rolatech-property-manage-layout', imports: [RouterLink, RouterLinkActive, MatButtonModule, RouterOutlet], encapsulation: ViewEncapsulation.None, template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 md:px-0 flex flex-row md:flex-col md:h-full items-center md:items-start h-16 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-1\" i18n>Property</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Basic info</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Media</a>\n <a routerLink=\"./video-tour\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Video tour</a>\n <a routerLink=\"./sections\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Details</a>\n <a routerLink=\"./location\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Location</a>\n <a routerLink=\"./features\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Features</a>\n <!-- <a routerLink=\"./amenities\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Amenities</a> -->\n <!-- <a routerLink=\"./tags\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Tags</a> -->\n <!-- <a routerLink=\"./options\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Options</a> -->\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Settings</div>\n <!-- <a routerLink=\"./variants\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Variants</a> -->\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Schedule</a>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Pricing</a>\n </div>\n <!-- <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Others</div>\n </div> -->\n @if (property) {\n @if (property.status.toString() === 'DRAFT') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"submit()\" i18n>Submit for review</button>\n </div>\n }\n @if (property.status.toString() === 'PENDING') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button disabled i18n>Pending</button>\n </div>\n }\n @if (property.status.toString() === 'APPROVED') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"publish()\" i18n>Publish</button>\n </div>\n }\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
2661
+ args: [{ selector: 'rolatech-property-manage-layout', imports: [RouterLink, RouterLinkActive, MatButtonModule, RouterOutlet], encapsulation: ViewEncapsulation.None, template: "<div class=\"flex flex-col md:flex-row m-auto\">\n <div\n class=\"min-w-[256px] px-3 md:px-0 flex flex-row md:flex-col md:h-full items-center md:items-start h-16 overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-1\" i18n>Property</div>\n <a routerLink=\"./info\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Basic info</a>\n <a routerLink=\"./media\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Media</a>\n <a routerLink=\"./videos\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Videos</a>\n <a routerLink=\"./sections\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Details</a>\n <a routerLink=\"./location\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Location</a>\n <a routerLink=\"./features\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Features</a>\n <a routerLink=\"./highlights\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Highlights</a>\n <a routerLink=\"./epc\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>EPC</a>\n <a routerLink=\"./translation\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Translation</a>\n <!-- <a routerLink=\"./amenities\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Amenities</a> -->\n <!-- <a routerLink=\"./tags\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Tags</a> -->\n <!-- <a routerLink=\"./options\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Options</a> -->\n </div>\n <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Settings</div>\n <!-- <a routerLink=\"./variants\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Variants</a> -->\n <a routerLink=\"./schedule\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Schedule</a>\n <a routerLink=\"./pricing\" routerLinkActive=\"manage-active\" class=\"p-2\" i18n>Pricing</a>\n </div>\n <!-- <div class=\"flex flex-row md:flex-col md:w-full\">\n <div class=\"hidden md:flex text-xl font-bold h-14 items-center px-2\" i18n>Others</div>\n </div> -->\n @if (property) {\n @if (property.status.toString() === 'DRAFT') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"submit()\" i18n>Submit for review</button>\n </div>\n }\n @if (property.status.toString() === 'PENDING') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button disabled i18n>Pending</button>\n </div>\n }\n @if (property.status.toString() === 'APPROVED') {\n <div class=\"md:mt-6 md:ml-2 flex items-center\">\n <button mat-flat-button (click)=\"publish()\" i18n>Publish</button>\n </div>\n }\n }\n </div>\n <div class=\"w-full\">\n <router-outlet></router-outlet>\n </div>\n</div>\n", styles: [".manage-active{background-color:var(--rt-10-percent-layer, rgba(0, 0, 0, .05));box-shadow:4px 0 var(--rt-base-background-inverse, #000) inset;font-weight:600}@media (max-width: 768px){.manage-active{box-shadow:inset 0 -4px 0 0 var(--rt-base-background-inverse, #000)}}.scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
2500
2662
  }] });
2501
2663
 
2502
2664
  class PropertyManageContentComponent {
@@ -3069,7 +3231,6 @@ class PropertyManageFeaturesComponent extends BaseComponent {
3069
3231
  features: this.featureService.find({}),
3070
3232
  options: this.propertyService.findFeatures(this.id),
3071
3233
  }).subscribe(({ features, options }) => {
3072
- console.log(options);
3073
3234
  this.features = features.data;
3074
3235
  if (options.data) {
3075
3236
  this.selected = this.convertToBinding(options.data);
@@ -3167,13 +3328,12 @@ class PropertyManageLocationComponent extends BaseComponent {
3167
3328
  longitude: '',
3168
3329
  latitude: '',
3169
3330
  }, ...(ngDevMode ? [{ debugName: "location" }] : []));
3170
- platformId = inject(PLATFORM_ID);
3171
3331
  placeAutocompleteElement;
3172
3332
  autocompleteInput;
3173
3333
  ngOnInit() {
3174
3334
  this.id = this.route.parent?.snapshot.paramMap.get('id');
3175
3335
  this.find();
3176
- if (isPlatformBrowser(this.platformId)) {
3336
+ if (this.isBrowser) {
3177
3337
  // this.autocompleteService = new google.maps.places.AutocompleteService();
3178
3338
  }
3179
3339
  }
@@ -3334,9 +3494,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImpor
3334
3494
  args: ['autocompleteInput', { static: false }]
3335
3495
  }] } });
3336
3496
 
3337
- class PropertyVideoTourAddComponent {
3338
- videoUrl = '';
3339
- videoId = '';
3497
+ class PropertyVideoYoutubeAddComponent {
3498
+ url = '';
3340
3499
  output = output();
3341
3500
  getYouTubeVideoId(url) {
3342
3501
  const regex = /(?:youtube\.com\/(?:watch\?v=|embed\/|v\/|shorts\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/;
@@ -3344,40 +3503,141 @@ class PropertyVideoTourAddComponent {
3344
3503
  return match ? match[1] : null;
3345
3504
  }
3346
3505
  ngDoCheck() {
3347
- this.videoId = this.getYouTubeVideoId(this.videoUrl);
3348
- this.output.emit(this.videoId);
3506
+ this.output.emit(this.url);
3349
3507
  }
3350
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoTourAddComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3351
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.0", type: PropertyVideoTourAddComponent, isStandalone: true, selector: "rolatech-property-video-tour-add", outputs: { output: "output" }, ngImport: i0, template: "<mat-form-field appearance=\"fill\">\n <mat-label>Youtube Video URL</mat-label>\n <input matInput placeholder=\"Youtube Video URL\" type=\"text\" [(ngModel)]=\"videoUrl\" i18n />\n</mat-form-field>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { 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$3.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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
3508
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoYoutubeAddComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3509
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.0", type: PropertyVideoYoutubeAddComponent, isStandalone: true, selector: "rolatech-property-video-youtube-add", outputs: { output: "output" }, ngImport: i0, template: "<mat-form-field appearance=\"fill\">\n <mat-label>Youtube Video URL</mat-label>\n <input matInput placeholder=\"Youtube Video URL\" type=\"text\" [(ngModel)]=\"url\" i18n />\n</mat-form-field>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { 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$3.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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
3352
3510
  }
3353
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoTourAddComponent, decorators: [{
3511
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoYoutubeAddComponent, decorators: [{
3354
3512
  type: Component,
3355
- args: [{ selector: 'rolatech-property-video-tour-add', imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule], template: "<mat-form-field appearance=\"fill\">\n <mat-label>Youtube Video URL</mat-label>\n <input matInput placeholder=\"Youtube Video URL\" type=\"text\" [(ngModel)]=\"videoUrl\" i18n />\n</mat-form-field>\n", styles: ["mat-form-field{width:100%}\n"] }]
3513
+ args: [{ selector: 'rolatech-property-video-youtube-add', imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule], template: "<mat-form-field appearance=\"fill\">\n <mat-label>Youtube Video URL</mat-label>\n <input matInput placeholder=\"Youtube Video URL\" type=\"text\" [(ngModel)]=\"url\" i18n />\n</mat-form-field>\n", styles: ["mat-form-field{width:100%}\n"] }]
3356
3514
  }] });
3357
3515
 
3358
- class PropertyManageVideoTourComponent extends BaseComponent {
3359
- propertyService = inject(PropertyService);
3516
+ class SafeUrlPipe {
3360
3517
  sanitizer = inject(DomSanitizer);
3361
- propertyVideoTour;
3518
+ transform(url) {
3519
+ if (!url) {
3520
+ return null;
3521
+ }
3522
+ // If you want to allow in [href], [src], [poster], [srcdoc], etc.
3523
+ return this.sanitizer.bypassSecurityTrustResourceUrl(url);
3524
+ }
3525
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: SafeUrlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
3526
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.0", ngImport: i0, type: SafeUrlPipe, isStandalone: true, name: "safeUrl" });
3527
+ }
3528
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: SafeUrlPipe, decorators: [{
3529
+ type: Pipe,
3530
+ args: [{
3531
+ name: 'safeUrl',
3532
+ standalone: true
3533
+ }]
3534
+ }] });
3535
+
3536
+ class PropertyVideoItem {
3537
+ platformId = inject(PLATFORM_ID);
3538
+ el = inject(ElementRef);
3539
+ dialogService = inject(DialogService);
3540
+ myVideo = viewChild('video', ...(ngDevMode ? [{ debugName: "myVideo" }] : []));
3541
+ progress = input(0, ...(ngDevMode ? [{ debugName: "progress" }] : []));
3542
+ video = input.required(...(ngDevMode ? [{ debugName: "video" }] : []));
3543
+ actions = input(true, ...(ngDevMode ? [{ debugName: "actions" }] : []));
3544
+ hasUnsaved = input(false, ...(ngDevMode ? [{ debugName: "hasUnsaved" }] : []));
3545
+ editId = model(...(ngDevMode ? [undefined, { debugName: "editId" }] : []));
3546
+ save = output();
3547
+ cancel = output();
3548
+ delete = output();
3549
+ edit = output();
3550
+ upload = output();
3551
+ editTitle = false;
3552
+ editing = false;
3553
+ onSave(video) {
3554
+ this.editTitle = false;
3555
+ this.editId.set(null);
3556
+ this.save.emit(video);
3557
+ }
3558
+ onCancel(video) {
3559
+ this.editTitle = false;
3560
+ this.editId.set(null);
3561
+ this.cancel.emit(video);
3562
+ }
3563
+ onDelete(video) {
3564
+ this.delete.emit(video);
3565
+ }
3566
+ onEdit(video) {
3567
+ this.editId.set(video.id);
3568
+ this.editTitle = true;
3569
+ this.edit.emit(video);
3570
+ }
3571
+ onMediaEdit(video) {
3572
+ this.editId.set(video.id);
3573
+ this.editing = true;
3574
+ }
3575
+ onUpload(id, data) {
3576
+ this.video().uploading = true;
3577
+ this.upload.emit({ id, data });
3578
+ }
3579
+ onLoadedMetadata(event) {
3580
+ if (this.video().duration) {
3581
+ return;
3582
+ }
3583
+ const videoElement = event.target;
3584
+ this.video().duration = videoElement.duration;
3585
+ }
3586
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
3587
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyVideoItem, isStandalone: true, selector: "rolatech-property-video-item", inputs: { progress: { classPropertyName: "progress", publicName: "progress", isSignal: true, isRequired: false, transformFunction: null }, video: { classPropertyName: "video", publicName: "video", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, hasUnsaved: { classPropertyName: "hasUnsaved", publicName: "hasUnsaved", isSignal: true, isRequired: false, transformFunction: null }, editId: { classPropertyName: "editId", publicName: "editId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { editId: "editIdChange", save: "save", cancel: "cancel", delete: "delete", edit: "edit", upload: "upload" }, viewQueries: [{ propertyName: "myVideo", first: true, predicate: ["video"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"flex flex-col grow p-2\">\n @if (!editTitle) {\n <div class=\"w-full flex gap-3 p-3 items-center\">\n <span>{{ video().title }}</span>\n <button mat-icon-button (click)=\"onEdit(video()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button (click)=\"onDelete(video()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n } @else {\n <div class=\"flex flex-col justify-between items-center\">\n <rolatech-input [(title)]=\"video().title\" placeholder=\"Title\"></rolatech-input>\n <div class=\"w-full flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onCancel(video())\" i18n>Cancel</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(video())\" i18n>Save</button>\n </div>\n </div>\n }\n\n <div>\n @if (video().url) {\n <div class=\"flex flex-col p-2 gap-3\">\n <div class=\"bg-black h-fit\">\n <video\n controls\n #videoContainer\n [src]=\"video().url\"\n class=\"w-full aspect-video\"\n (loadedmetadata)=\"onLoadedMetadata($event)\"\n [poster]=\"video().thumbnail\"\n >\n <!-- <source [src]=\"video().url\" type=\"video/mp4\" /> -->\n </video>\n </div>\n <div class=\"flex flex-col justify-between w-full\">\n <div class=\"flex justify-between items-center w-full\">\n @if (video().uploading) {\n <div>\n <span> {{ video().progress }}%</span>\n </div>\n }\n </div>\n <div class=\"flex justify-between items-center\">\n <div>\n <span>{{ video().duration | duration }}</span>\n </div>\n </div>\n </div>\n </div>\n @if (video().uploading) {\n <div class=\"p-2\">\n <mat-progress-bar mode=\"determinate\" [value]=\"video().progress\"> </mat-progress-bar>\n </div>\n } } @else {\n <div class=\"px-3\">\n <input\n class=\"ud-sr-only\"\n type=\"file\"\n accept=\".avi,.mpg,.mpeg,.flv,.mov,.m2v,.m4v,.mp4,.rm,.ram,.vob,.ogv,.webm,.wmv\"\n (change)=\"onUpload(video().id, $event)\"\n #fileInput\n />\n <div class=\"flex justify-between items-center\">\n <div i18n>No videos</div>\n <button mat-flat-button class=\"max-h-8\" (click)=\"fileInput.click()\" i18n>Upload</button>\n </div>\n </div>\n }\n </div>\n @if (!hasUnsaved) {\n <ng-content></ng-content>\n }\n</div>\n", styles: [".ud-sr-only{position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px,1px,1px,1px)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.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$4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: i8.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: InputComponent, selector: "rolatech-input", inputs: ["title", "placeholder"], outputs: ["titleChange"] }, { kind: "pipe", type: DurationPipe, name: "duration" }] });
3588
+ }
3589
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyVideoItem, decorators: [{
3590
+ type: Component,
3591
+ args: [{ selector: 'rolatech-property-video-item', imports: [
3592
+ MatIconModule,
3593
+ AngularCommonModule,
3594
+ FormsModule,
3595
+ MatButtonModule,
3596
+ MatDividerModule,
3597
+ AngularComponentsModule,
3598
+ DurationPipe,
3599
+ InputComponent
3600
+ ], template: "<div class=\"flex flex-col grow p-2\">\n @if (!editTitle) {\n <div class=\"w-full flex gap-3 p-3 items-center\">\n <span>{{ video().title }}</span>\n <button mat-icon-button (click)=\"onEdit(video()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>edit</mat-icon>\n </button>\n <button mat-icon-button (click)=\"onDelete(video()); $event.stopPropagation()\" class=\"max-w-8 max-h-8 !p-1\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n } @else {\n <div class=\"flex flex-col justify-between items-center\">\n <rolatech-input [(title)]=\"video().title\" placeholder=\"Title\"></rolatech-input>\n <div class=\"w-full flex flex-row justify-end p-3 gap-3\">\n <button mat-button class=\"max-h-8\" (click)=\"onCancel(video())\" i18n>Cancel</button>\n <button mat-flat-button class=\"max-h-8\" (click)=\"onSave(video())\" i18n>Save</button>\n </div>\n </div>\n }\n\n <div>\n @if (video().url) {\n <div class=\"flex flex-col p-2 gap-3\">\n <div class=\"bg-black h-fit\">\n <video\n controls\n #videoContainer\n [src]=\"video().url\"\n class=\"w-full aspect-video\"\n (loadedmetadata)=\"onLoadedMetadata($event)\"\n [poster]=\"video().thumbnail\"\n >\n <!-- <source [src]=\"video().url\" type=\"video/mp4\" /> -->\n </video>\n </div>\n <div class=\"flex flex-col justify-between w-full\">\n <div class=\"flex justify-between items-center w-full\">\n @if (video().uploading) {\n <div>\n <span> {{ video().progress }}%</span>\n </div>\n }\n </div>\n <div class=\"flex justify-between items-center\">\n <div>\n <span>{{ video().duration | duration }}</span>\n </div>\n </div>\n </div>\n </div>\n @if (video().uploading) {\n <div class=\"p-2\">\n <mat-progress-bar mode=\"determinate\" [value]=\"video().progress\"> </mat-progress-bar>\n </div>\n } } @else {\n <div class=\"px-3\">\n <input\n class=\"ud-sr-only\"\n type=\"file\"\n accept=\".avi,.mpg,.mpeg,.flv,.mov,.m2v,.m4v,.mp4,.rm,.ram,.vob,.ogv,.webm,.wmv\"\n (change)=\"onUpload(video().id, $event)\"\n #fileInput\n />\n <div class=\"flex justify-between items-center\">\n <div i18n>No videos</div>\n <button mat-flat-button class=\"max-h-8\" (click)=\"fileInput.click()\" i18n>Upload</button>\n </div>\n </div>\n }\n </div>\n @if (!hasUnsaved) {\n <ng-content></ng-content>\n }\n</div>\n", styles: [".ud-sr-only{position:absolute!important;height:1px;width:1px;overflow:hidden;clip:rect(1px,1px,1px,1px)}\n"] }]
3601
+ }] });
3602
+
3603
+ class PropertyManageVideoComponent extends BaseComponent {
3604
+ propertyService = inject(PropertyService);
3605
+ tabIndex = signal(0, ...(ngDevMode ? [{ debugName: "tabIndex" }] : []));
3606
+ videos = [];
3362
3607
  data;
3363
3608
  iframeVisible = false;
3364
3609
  iframeLoaded = false;
3365
- safeURL = this.sanitizer.bypassSecurityTrustResourceUrl('');
3366
3610
  thumbnailUrl;
3367
- type = PropertyVideoTourType;
3611
+ provider = PropertyVideoProvider;
3368
3612
  chunkSize = 5 * 1024 * 1024; // 切片大小
3613
+ selectProvider = '';
3614
+ videoEditId = '';
3615
+ get youtubeVideos() {
3616
+ return this.videos.filter(v => v.provider === PropertyVideoProvider.YOUTUBE);
3617
+ }
3618
+ get cosVideos() {
3619
+ return this.videos.filter(v => v.provider === PropertyVideoProvider.COS);
3620
+ }
3621
+ constructor() {
3622
+ super();
3623
+ effect(() => {
3624
+ const index = this.tabIndex();
3625
+ if (index === 0) {
3626
+ this.selectProvider = 'YOUTUBE';
3627
+ }
3628
+ if (index === 1) {
3629
+ this.selectProvider = 'COS';
3630
+ }
3631
+ });
3632
+ }
3369
3633
  ngOnInit() {
3370
3634
  this.id = this.route.parent?.snapshot.paramMap.get('id');
3371
- this.getVideoTour();
3635
+ this.getVideos();
3372
3636
  }
3373
- getVideoTour() {
3374
- this.propertyService.getVideoTour(this.id).subscribe({
3637
+ getVideos() {
3638
+ this.propertyService.getVideos(this.id).subscribe({
3375
3639
  next: (res) => {
3376
- this.propertyVideoTour = res.data;
3377
- // this.safeURL = this.sanitizer.bypassSecurityTrustResourceUrl(
3378
- // `https://www.youtube.com/embed/${this.propertyVideoTour.videoId}`,
3379
- // );
3380
- // this.thumbnailUrl = `https://i.ytimg.com/vi/${this.propertyVideoTour.videoId}/hqdefault.jpg`;
3640
+ this.videos = Array.isArray(res.data) ? res.data : [];
3381
3641
  },
3382
3642
  });
3383
3643
  }
@@ -3387,68 +3647,129 @@ class PropertyManageVideoTourComponent extends BaseComponent {
3387
3647
  onIframeLoad() {
3388
3648
  this.iframeLoaded = true;
3389
3649
  }
3390
- addVideoTour() {
3391
- const features = {
3392
- title: 'Add video tour',
3650
+ addYoutubeUrl() {
3651
+ const options = {
3652
+ title: 'Add Youtube url',
3393
3653
  cancelText: 'Cancel',
3394
3654
  confirmText: 'Confirm',
3395
3655
  data: {
3396
- videoUrl: '',
3656
+ url: '',
3397
3657
  },
3398
- component: PropertyVideoTourAddComponent,
3658
+ component: PropertyVideoYoutubeAddComponent,
3399
3659
  };
3400
- this.dialogService.open(features);
3660
+ this.dialogService.open(options);
3401
3661
  this.dialogService.confirmed().subscribe({
3402
3662
  next: (res) => {
3403
3663
  if (res) {
3404
3664
  const data = {
3405
- videoId: res,
3665
+ url: res,
3666
+ title: 'Youtube',
3667
+ provider: PropertyVideoProvider.YOUTUBE
3406
3668
  };
3407
- this.propertyService.addVideoTour(this.id, data).subscribe({
3669
+ this.propertyService.addVideo(this.id, data).subscribe({
3408
3670
  next: (res) => {
3409
- this.propertyVideoTour = res.data;
3410
- this.snackBarService.open('Video tour added');
3671
+ this.videos.push(res.data);
3672
+ this.snackBarService.open('Video added');
3411
3673
  },
3412
3674
  });
3413
3675
  }
3414
3676
  },
3415
3677
  });
3416
3678
  }
3417
- updateVideoTour() {
3418
- const features = {
3419
- title: 'Update video tour',
3679
+ updateYoutubeUrl(video) {
3680
+ const options = {
3681
+ title: 'Update Youtube url',
3420
3682
  cancelText: 'Cancel',
3421
3683
  confirmText: 'Confirm',
3422
3684
  data: {
3423
- videoId: '',
3685
+ url: video?.url || '',
3424
3686
  },
3425
- component: PropertyVideoTourAddComponent,
3687
+ component: PropertyVideoYoutubeAddComponent,
3426
3688
  };
3427
- this.dialogService.open(features);
3689
+ this.dialogService.open(options);
3428
3690
  this.dialogService.confirmed().subscribe({
3429
3691
  next: (res) => {
3430
3692
  if (res) {
3431
3693
  const data = {
3432
- videoId: res,
3694
+ url: res,
3695
+ title: video.title
3433
3696
  };
3434
- this.propertyService.addVideoTour(this.id, data).subscribe({
3697
+ this.propertyService.updateVideo(this.id, video.id, data).subscribe({
3435
3698
  next: (res) => {
3436
- this.propertyVideoTour = res.data;
3437
- this.snackBarService.open('Update successfully');
3699
+ video.url = res.data.url;
3700
+ // Force reload
3701
+ this.iframeLoaded = false;
3702
+ this.snackBarService.open('Youtube url updated');
3703
+ },
3704
+ });
3705
+ }
3706
+ },
3707
+ });
3708
+ }
3709
+ toYoutubeEmbed(urlOrId) {
3710
+ const m = urlOrId.match(/(?:v=|\/embed\/|\.be\/)([A-Za-z0-9_-]{6,})/);
3711
+ const id = m ? m[1] : urlOrId;
3712
+ return `https://www.youtube-nocookie.com/embed/${id}`;
3713
+ }
3714
+ addCOSVideo() {
3715
+ const video = {
3716
+ provider: PropertyVideoProvider.COS
3717
+ };
3718
+ this.propertyService.addVideo(this.id, video).subscribe({
3719
+ next: (res) => {
3720
+ this.videoEditId = res.data.id;
3721
+ this.videos.push(res.data);
3722
+ },
3723
+ });
3724
+ }
3725
+ onCOSVideoSave(event) {
3726
+ const { id, title, url } = event;
3727
+ const data = {
3728
+ title, url
3729
+ };
3730
+ this.propertyService.updateVideo(this.id, id, data).subscribe({
3731
+ next: (res) => {
3732
+ this.snackBarService.open('Youtube url updated');
3733
+ },
3734
+ });
3735
+ }
3736
+ onCOSVideoDelete(video, event) {
3737
+ if (!video.id) {
3738
+ return;
3739
+ }
3740
+ const options = {
3741
+ title: 'Confirm delete',
3742
+ message: 'Are you sure?',
3743
+ cancelText: 'Cancel',
3744
+ confirmText: 'Ok',
3745
+ };
3746
+ this.dialogService.open(options);
3747
+ this.dialogService.confirmed().subscribe({
3748
+ next: (res) => {
3749
+ if (res) {
3750
+ this.propertyService.deleteVideo(this.id, video.id).subscribe({
3751
+ next: res => {
3752
+ this.videos = this.videos.filter(v => v.id !== video.id);
3753
+ this.snackBarService.open("Video deleted");
3754
+ },
3755
+ error: (error) => {
3756
+ this.snackBarService.open(error.message);
3438
3757
  },
3439
3758
  });
3440
3759
  }
3441
3760
  },
3442
3761
  });
3443
3762
  }
3444
- onUploadToCOS(event) {
3763
+ onVideoEdit(event) { }
3764
+ onMediaDelete(video, event) { }
3765
+ onCOSVideoUpload(video, event) {
3445
3766
  const file = event.data.target.files[0];
3446
3767
  if (!file) {
3447
3768
  return;
3448
3769
  }
3449
- const item = {
3450
- url: URL.createObjectURL(file),
3451
- };
3770
+ video.url = URL.createObjectURL(file);
3771
+ video.progress = 0;
3772
+ video.uploading = true;
3452
3773
  const reader = new FileReader();
3453
3774
  if (reader.readyState === FileReader.EMPTY) {
3454
3775
  reader.onload = async (e) => {
@@ -3456,10 +3777,10 @@ class PropertyManageVideoTourComponent extends BaseComponent {
3456
3777
  crypto.subtle.digest('SHA-256', fileBuffer).then((res) => {
3457
3778
  const hashArray = Array.from(new Uint8Array(res));
3458
3779
  const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('');
3459
- const data = { hash: hashHex, filename: file.name, fileType: file.type };
3460
- this.propertyService.uploadVideoTourInit(this.id, data).subscribe({
3780
+ const data = { hash: hashHex, filename: file.name, fileType: file.type, type: 'COS' };
3781
+ this.propertyService.uploadVideosInit(this.id, data).subscribe({
3461
3782
  next: (res) => {
3462
- this.uploadPart(null, res.data.uploadId, file);
3783
+ this.uploadPart(video, res.data.uploadId, file);
3463
3784
  },
3464
3785
  error: (e) => { },
3465
3786
  });
@@ -3469,34 +3790,33 @@ class PropertyManageVideoTourComponent extends BaseComponent {
3469
3790
  reader.readAsArrayBuffer(file);
3470
3791
  }
3471
3792
  }
3472
- uploadPart(lecture, uploadId, file) {
3793
+ uploadPart(video, uploadId, file) {
3473
3794
  let uploadedCount = 0;
3474
3795
  const fileChunkList = this.createFileChunk(file);
3475
3796
  const numberOfChunks = fileChunkList.length;
3476
3797
  from(fileChunkList)
3477
- .pipe(concatMap((val, index) => this.uploadFile(lecture, val.file, index, uploadId)), take(numberOfChunks))
3798
+ .pipe(concatMap((val, index) => this.uploadFile(video, val.file, index, uploadId)), take(numberOfChunks))
3478
3799
  .subscribe((res) => {
3479
3800
  uploadedCount++;
3480
3801
  const p = ((uploadedCount / numberOfChunks) * 100).toFixed(0);
3481
- lecture.item.progress = parseFloat(p);
3802
+ video.progress = parseFloat(p);
3482
3803
  if (uploadedCount === numberOfChunks) {
3483
- this.completePartUpload(lecture, { uploadId });
3804
+ this.completePartUpload(video, { uploadId });
3484
3805
  }
3485
3806
  });
3486
3807
  }
3487
- completePartUpload(lecture, data) {
3488
- data.duration = lecture.item.duration;
3489
- this.propertyService.completePartUpload(lecture.id, data).subscribe({
3808
+ completePartUpload(video, data) {
3809
+ data.duration = video.duration;
3810
+ this.propertyService.completePartUpload(video.id || '', data).subscribe({
3490
3811
  next: (res) => {
3491
- lecture.isUploading = false;
3492
- lecture.item.id = res.data.id;
3812
+ video.uploading = false;
3493
3813
  },
3494
3814
  error: (e) => {
3495
3815
  this.snackBarService.open('Upload failed: ' + e.message);
3496
3816
  },
3497
3817
  });
3498
3818
  }
3499
- uploadFile(lecture, file, index, uploadId) {
3819
+ uploadFile(property, file, index, uploadId) {
3500
3820
  const reader = new FileReader();
3501
3821
  return new Observable((observer) => {
3502
3822
  reader.readAsArrayBuffer(file);
@@ -3510,7 +3830,8 @@ class PropertyManageVideoTourComponent extends BaseComponent {
3510
3830
  formData.append('uploadId', uploadId);
3511
3831
  formData.append('number', index + 1);
3512
3832
  formData.append('hash', hashHex);
3513
- this.propertyService.uploadVideoTourPartsToProperty(lecture.id, formData).subscribe({
3833
+ formData.append('type', 'COS');
3834
+ this.propertyService.uploadVideosPartsToProperty(property.id, formData).subscribe({
3514
3835
  next: (res) => {
3515
3836
  observer.next(res);
3516
3837
  observer.complete();
@@ -3537,12 +3858,91 @@ class PropertyManageVideoTourComponent extends BaseComponent {
3537
3858
  }
3538
3859
  return fileChunkList;
3539
3860
  }
3540
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageVideoTourComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3541
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyManageVideoTourComponent, isStandalone: true, selector: "rolatech-property-manage-video-tour", usesInheritance: true, ngImport: i0, template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"Video tour\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <!-- <rolatech-property-video-tour-item [property]=\"\"></rolatech-property-video-tour-item> -->\n <div class=\"flex flex-col\">\n <div>\n <div>Youtube</div>\n <div></div>\n </div>\n <div>\n <div>COS</div>\n <div></div>\n </div>\n </div>\n <rolatech-video-upload (upload)=\"onUploadToCOS($event)\"></rolatech-video-upload>\n @for (item of propertyVideoTour; track $index) {\n <div>{{ type[item.type] === 'Youtube' }}</div>\n @if (type[item.type] === 'Youtube') {\n <div class=\"relative w-full aspect-video\">\n @if (!iframeLoaded) {\n <div class=\"absolute inset-0 flex items-center justify-center bg-black/10\">\n <span>Loading...</span>\n </div>\n }\n <iframe [src]=\"safeURL\" frameborder=\"0\" allowfullscreen class=\"w-full h-full\" (load)=\"iframeLoaded = true\"></iframe>\n </div>\n <button mat-flat-button (click)=\"updateVideoTour()\" class=\"mt-3\">Update video tour</button>\n }\n @if (type[item.type] === 'COS') {}\n }\n <!-- @if (propertyVideoTour) {\n\n } @else {\n <button mat-flat-button (click)=\"addVideoTour()\">Add video tour</button>\n } -->\n</rolatech-property-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: PropertyManageContentComponent, selector: "rolatech-property-manage-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.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: VideoUpload, selector: "rolatech-video-upload", inputs: ["item", "uploading", "editing", "progress", "editId", "hasUnsaved"], outputs: ["uploadingChange", "editingChange", "mediaEdit", "thumbnailUpload", "upload", "deleteMedia", "editIdChange"] }] });
3861
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageVideoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3862
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyManageVideoComponent, isStandalone: true, selector: "rolatech-property-manage-video", usesInheritance: true, ngImport: i0, template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"Videos\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <rolatech-tabs [(select)]=\"tabIndex\">\n <rolatech-tab label=\"Youtube\"></rolatech-tab>\n <rolatech-tab label=\"COS\"></rolatech-tab>\n </rolatech-tabs>\n <div class=\"py-2\">\n @if (tabIndex() === 0) {\n @if (youtubeVideos.length > 0 && youtubeVideos[0].url) {\n <div class=\"relative w-full aspect-video\">\n @if (!iframeLoaded) {\n <div class=\"absolute inset-0 flex items-center justify-center bg-black/10 border bg-white\">\n <span>Loading...</span>\n </div>\n }\n <iframe\n [src]=\"toYoutubeEmbed(youtubeVideos[0].url) | safeUrl\"\n frameborder=\"0\"\n allowfullscreen\n class=\"w-full h-full\"\n (load)=\"iframeLoaded = true\"\n ></iframe>\n </div>\n <button mat-flat-button (click)=\"updateYoutubeUrl(youtubeVideos[0])\" class=\"mt-3\">Update Youtube url</button>\n } @else {\n <button mat-flat-button (click)=\"addYoutubeUrl()\" class=\"mt-3\">Add Youtube url</button>\n }\n }\n @if (tabIndex() === 1) {\n @for (video of cosVideos; track $index) {\n <rolatech-property-video-item\n [video]=\"video\"\n (save)=\"onCOSVideoSave($event)\"\n (delete)=\"onCOSVideoDelete(video, $event)\"\n (edit)=\"onVideoEdit($event)\"\n (upload)=\"onCOSVideoUpload(video, $event)\"\n [editId]=\"videoEditId\"\n ></rolatech-property-video-item>\n }\n\n <div class=\"p-3\">\n <button mat-flat-button class=\"max-h-8\" (click)=\"addCOSVideo()\">\n <mat-icon>add</mat-icon>\n <span>Add video</span>\n </button>\n </div>\n <!-- <rolatech-video-upload (upload)=\"onMediaUploadInit($event)\"></rolatech-video-upload> -->\n }\n </div>\n</rolatech-property-manage-content>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: PropertyManageContentComponent, selector: "rolatech-property-manage-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$4.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: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { kind: "component", type: PropertyVideoItem, selector: "rolatech-property-video-item", inputs: ["progress", "video", "actions", "hasUnsaved", "editId"], outputs: ["editIdChange", "save", "cancel", "delete", "edit", "upload"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: SafeUrlPipe, name: "safeUrl" }] });
3542
3863
  }
3543
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageVideoTourComponent, decorators: [{
3864
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageVideoComponent, decorators: [{
3544
3865
  type: Component,
3545
- args: [{ selector: 'rolatech-property-manage-video-tour', imports: [ToolbarComponent, PropertyManageContentComponent, MatButtonModule, VideoUpload], template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"Video tour\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <!-- <rolatech-property-video-tour-item [property]=\"\"></rolatech-property-video-tour-item> -->\n <div class=\"flex flex-col\">\n <div>\n <div>Youtube</div>\n <div></div>\n </div>\n <div>\n <div>COS</div>\n <div></div>\n </div>\n </div>\n <rolatech-video-upload (upload)=\"onUploadToCOS($event)\"></rolatech-video-upload>\n @for (item of propertyVideoTour; track $index) {\n <div>{{ type[item.type] === 'Youtube' }}</div>\n @if (type[item.type] === 'Youtube') {\n <div class=\"relative w-full aspect-video\">\n @if (!iframeLoaded) {\n <div class=\"absolute inset-0 flex items-center justify-center bg-black/10\">\n <span>Loading...</span>\n </div>\n }\n <iframe [src]=\"safeURL\" frameborder=\"0\" allowfullscreen class=\"w-full h-full\" (load)=\"iframeLoaded = true\"></iframe>\n </div>\n <button mat-flat-button (click)=\"updateVideoTour()\" class=\"mt-3\">Update video tour</button>\n }\n @if (type[item.type] === 'COS') {}\n }\n <!-- @if (propertyVideoTour) {\n\n } @else {\n <button mat-flat-button (click)=\"addVideoTour()\">Add video tour</button>\n } -->\n</rolatech-property-manage-content>\n" }]
3866
+ args: [{ selector: 'rolatech-property-manage-video', imports: [CommonModule, ToolbarComponent, PropertyManageContentComponent, MatButtonModule, TabsComponent, TabComponent, SafeUrlPipe, PropertyVideoItem, MatIcon], template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"Videos\" class=\"hidden md:block\" divider> </rolatech-toolbar>\n <rolatech-tabs [(select)]=\"tabIndex\">\n <rolatech-tab label=\"Youtube\"></rolatech-tab>\n <rolatech-tab label=\"COS\"></rolatech-tab>\n </rolatech-tabs>\n <div class=\"py-2\">\n @if (tabIndex() === 0) {\n @if (youtubeVideos.length > 0 && youtubeVideos[0].url) {\n <div class=\"relative w-full aspect-video\">\n @if (!iframeLoaded) {\n <div class=\"absolute inset-0 flex items-center justify-center bg-black/10 border bg-white\">\n <span>Loading...</span>\n </div>\n }\n <iframe\n [src]=\"toYoutubeEmbed(youtubeVideos[0].url) | safeUrl\"\n frameborder=\"0\"\n allowfullscreen\n class=\"w-full h-full\"\n (load)=\"iframeLoaded = true\"\n ></iframe>\n </div>\n <button mat-flat-button (click)=\"updateYoutubeUrl(youtubeVideos[0])\" class=\"mt-3\">Update Youtube url</button>\n } @else {\n <button mat-flat-button (click)=\"addYoutubeUrl()\" class=\"mt-3\">Add Youtube url</button>\n }\n }\n @if (tabIndex() === 1) {\n @for (video of cosVideos; track $index) {\n <rolatech-property-video-item\n [video]=\"video\"\n (save)=\"onCOSVideoSave($event)\"\n (delete)=\"onCOSVideoDelete(video, $event)\"\n (edit)=\"onVideoEdit($event)\"\n (upload)=\"onCOSVideoUpload(video, $event)\"\n [editId]=\"videoEditId\"\n ></rolatech-property-video-item>\n }\n\n <div class=\"p-3\">\n <button mat-flat-button class=\"max-h-8\" (click)=\"addCOSVideo()\">\n <mat-icon>add</mat-icon>\n <span>Add video</span>\n </button>\n </div>\n <!-- <rolatech-video-upload (upload)=\"onMediaUploadInit($event)\"></rolatech-video-upload> -->\n }\n </div>\n</rolatech-property-manage-content>\n" }]
3867
+ }], ctorParameters: () => [] });
3868
+
3869
+ class PropertyManageHighlights extends BaseComponent {
3870
+ propertyService = inject(PropertyService);
3871
+ highlights = [];
3872
+ ngOnInit() {
3873
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
3874
+ this.find();
3875
+ }
3876
+ find() {
3877
+ this.propertyService.getHighlights(this.id).subscribe({
3878
+ next: (res) => {
3879
+ this.highlights = res.data;
3880
+ }
3881
+ });
3882
+ }
3883
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageHighlights, deps: null, target: i0.ɵɵFactoryTarget.Component });
3884
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: PropertyManageHighlights, isStandalone: true, selector: "rolatech-property-manage-highlights", usesInheritance: true, ngImport: i0, template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"Highlights\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div>\n @for (item of highlights; track $index) {\n <div class=\"font-bold p-1\">{{item}}</div>\n }\n </div>\n</rolatech-property-manage-content>\n", styles: [""], dependencies: [{ kind: "component", type: PropertyManageContentComponent, selector: "rolatech-property-manage-content" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }] });
3885
+ }
3886
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageHighlights, decorators: [{
3887
+ type: Component,
3888
+ args: [{ selector: 'rolatech-property-manage-highlights', imports: [PropertyManageContentComponent, ToolbarComponent], template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"Highlights\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div>\n @for (item of highlights; track $index) {\n <div class=\"font-bold p-1\">{{item}}</div>\n }\n </div>\n</rolatech-property-manage-content>\n" }]
3889
+ }] });
3890
+
3891
+ class PropertyManageEpc extends BaseComponent {
3892
+ propertyService = inject(PropertyService);
3893
+ currentRating = '';
3894
+ potentialRating = '';
3895
+ currentScore = 0;
3896
+ potentialScore = 0;
3897
+ ngOnInit() {
3898
+ this.id = this.route.parent?.snapshot.paramMap.get('id');
3899
+ this.get();
3900
+ }
3901
+ get() {
3902
+ this.propertyService.getEpc(this.id).subscribe({
3903
+ next: (res) => {
3904
+ this.currentScore = res.data.currentScore || 0;
3905
+ this.potentialScore = res.data.potentialScore || 0;
3906
+ }
3907
+ });
3908
+ }
3909
+ add() {
3910
+ const data = {
3911
+ currentScore: this.currentScore,
3912
+ potentialScore: this.potentialScore
3913
+ };
3914
+ this.propertyService.addEpc(this.id, data).subscribe({
3915
+ next: res => {
3916
+ this.snackBarService.open("EPC Added");
3917
+ }
3918
+ });
3919
+ }
3920
+ update() {
3921
+ const data = {
3922
+ currentScore: this.currentScore,
3923
+ potentialScore: this.potentialScore
3924
+ };
3925
+ this.propertyService.updateEpc(this.id, data).subscribe({
3926
+ next: res => {
3927
+ this.snackBarService.open("EPC Updated");
3928
+ }
3929
+ });
3930
+ }
3931
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageEpc, deps: null, target: i0.ɵɵFactoryTarget.Component });
3932
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.0", type: PropertyManageEpc, isStandalone: true, selector: "rolatech-property-manage-epc", usesInheritance: true, ngImport: i0, template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"EPC\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div class=\"w-full md:max-w-[70%]\">\n <div class=\"p-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Current Score</mat-label>\n <input matInput [(ngModel)]=\"currentScore\" placeholder=\"Current Score\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>Potential Score</mat-label>\n <input matInput [(ngModel)]=\"potentialScore\" placeholder=\"Potential Score\" />\n </mat-form-field>\n <button mat-flat-button (click)=\"add()\">Save</button>\n </div>\n <div>\n <div class=\"font-bold text-lg p-2\">Domestic EPC</div>\n <rolatech-epc-chart\n [currentScore]=\"currentScore\"\n [potentialScore]=\"potentialScore\"\n [showCaptions]=\"true\"\n ></rolatech-epc-chart>\n </div>\n </div>\n</rolatech-property-manage-content>\n", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: PropertyManageContentComponent, selector: "rolatech-property-manage-content" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: EpcChart, selector: "rolatech-epc-chart", inputs: ["currentScore", "potentialScore", "showCaptions"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.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: MatButtonModule }, { kind: "component", type: i1$4.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"] }] });
3933
+ }
3934
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageEpc, decorators: [{
3935
+ type: Component,
3936
+ args: [{ selector: 'rolatech-property-manage-epc', imports: [PropertyManageContentComponent, ToolbarComponent, EpcChart, MatFormFieldModule, FormsModule, MatInputModule, MatButtonModule], template: "<rolatech-property-manage-content>\n <rolatech-toolbar title=\"EPC\" class=\"hidden md:block\" divider></rolatech-toolbar>\n <div class=\"w-full md:max-w-[70%]\">\n <div class=\"p-2\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Current Score</mat-label>\n <input matInput [(ngModel)]=\"currentScore\" placeholder=\"Current Score\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\">\n <mat-label>Potential Score</mat-label>\n <input matInput [(ngModel)]=\"potentialScore\" placeholder=\"Potential Score\" />\n </mat-form-field>\n <button mat-flat-button (click)=\"add()\">Save</button>\n </div>\n <div>\n <div class=\"font-bold text-lg p-2\">Domestic EPC</div>\n <rolatech-epc-chart\n [currentScore]=\"currentScore\"\n [potentialScore]=\"potentialScore\"\n [showCaptions]=\"true\"\n ></rolatech-epc-chart>\n </div>\n </div>\n</rolatech-property-manage-content>\n", styles: ["mat-form-field{width:100%}\n"] }]
3937
+ }] });
3938
+
3939
+ class PropertyManageTranslation {
3940
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageTranslation, deps: [], target: i0.ɵɵFactoryTarget.Component });
3941
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.0", type: PropertyManageTranslation, isStandalone: true, selector: "rolatech-property-manage-translation", ngImport: i0, template: "<p>property-manage-translation works!</p>\n", styles: [""] });
3942
+ }
3943
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: PropertyManageTranslation, decorators: [{
3944
+ type: Component,
3945
+ args: [{ selector: 'rolatech-property-manage-translation', imports: [], template: "<p>property-manage-translation works!</p>\n" }]
3546
3946
  }] });
3547
3947
 
3548
3948
  const propertyManageRoutes = [
@@ -3567,8 +3967,8 @@ const propertyManageRoutes = [
3567
3967
  component: PropertyManageMediaComponent,
3568
3968
  },
3569
3969
  {
3570
- path: 'video-tour',
3571
- component: PropertyManageVideoTourComponent,
3970
+ path: 'videos',
3971
+ component: PropertyManageVideoComponent,
3572
3972
  },
3573
3973
  {
3574
3974
  path: 'sections',
@@ -3582,6 +3982,18 @@ const propertyManageRoutes = [
3582
3982
  path: 'features',
3583
3983
  component: PropertyManageFeaturesComponent,
3584
3984
  },
3985
+ {
3986
+ path: 'highlights',
3987
+ component: PropertyManageHighlights,
3988
+ },
3989
+ {
3990
+ path: 'epc',
3991
+ component: PropertyManageEpc,
3992
+ },
3993
+ {
3994
+ path: 'translation',
3995
+ component: PropertyManageTranslation,
3996
+ },
3585
3997
  // {
3586
3998
  // path: 'amenities',
3587
3999
  // component: PropertyManageAmenitiesComponent,
@@ -3696,7 +4108,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImpor
3696
4108
  const propertyManageViewingsRoutes = [
3697
4109
  {
3698
4110
  path: '',
3699
- loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-gVG8HPPp.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
4111
+ loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-Bk02i8QS.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
3700
4112
  },
3701
4113
  {
3702
4114
  path: ':id',
@@ -3942,7 +4354,7 @@ class LettingManageDetail extends BaseComponent {
3942
4354
  // const updatedCriteria = matchingVariant ? updateCriteriaFromVariant(matchingVariant) : [];
3943
4355
  }
3944
4356
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: LettingManageDetail, deps: [], target: i0.ɵɵFactoryTarget.Component });
3945
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: LettingManageDetail, isStandalone: true, selector: "rolatech-letting-manage-detail", usesInheritance: true, ngImport: i0, template: "@if (property) {\n<rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <rolatech-property-features [features]=\"property.features\"></rolatech-property-features>\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videoTours) {\n <rolatech-property-video-tour [videoTour]=\"property.videoTours[0]\"></rolatech-property-video-tour>\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 </div>\n </div>\n <div class=\"w-full h-fit md:w-1/3 rounded-md border shadow p-3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n subtitle=\"PrimeCase Agent\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-letting-actions\n [letting]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-letting-action-contact [phone]=\"user.phone\"></rolatech-letting-action-contact>\n }\n </rolatech-letting-actions>\n </div>\n </div>\n </div>\n</rolatech-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: PropertyInfoComponent, selector: "rolatech-property-info", inputs: ["property", "inWishList"], outputs: ["wish"] }, { kind: "component", type: PropertyMediaComponent, selector: "rolatech-property-media", inputs: ["media", "min"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertySectionComponent, selector: "rolatech-property-section", inputs: ["section", "user", "username"] }, { kind: "component", type: LettingActions, selector: "rolatech-letting-actions", inputs: ["letting"], outputs: ["requestViewing", "offer", "deposit"] }, { kind: "component", type: LettingActionContact, selector: "rolatech-letting-action-contact", inputs: ["phone"], outputs: ["callAgent", "emailAgent"] }, { kind: "component", type: PropertyLocationComponent, selector: "rolatech-property-location", inputs: ["location"] }, { kind: "component", type: PropertyFeaturesComponent, selector: "rolatech-property-features", inputs: ["features"] }, { kind: "component", type: PropertyVideoTourComponent, selector: "rolatech-property-video-tour", inputs: ["videoTour"] }, { kind: "component", type: PropertyAgentRenderer, selector: "rolatech-property-agent-renderer", inputs: ["name", "avatar", "username", "subtitle"] }] });
4357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.0", type: LettingManageDetail, isStandalone: true, selector: "rolatech-letting-manage-detail", usesInheritance: true, ngImport: i0, template: "@if (property) {\n<rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <rolatech-property-features [features]=\"property.features\"></rolatech-property-features>\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videos) {\n <rolatech-property-video [video]=\"property.videos[0]\"></rolatech-property-video>\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 </div>\n </div>\n <div class=\"w-full h-fit md:w-1/3 rounded-md border shadow p-3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n subtitle=\"PrimeCase Agent\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-letting-actions\n [letting]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-letting-action-contact [phone]=\"user.phone\"></rolatech-letting-action-contact>\n }\n </rolatech-letting-actions>\n </div>\n </div>\n </div>\n</rolatech-container>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: AngularCommonModule }, { kind: "ngmodule", type: AngularComponentsModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: PropertyInfoComponent, selector: "rolatech-property-info", inputs: ["property", "inWishList"], outputs: ["wish"] }, { kind: "component", type: PropertyMediaComponent, selector: "rolatech-property-media", inputs: ["media", "min"] }, { kind: "component", type: PropertyPricingComponent, selector: "rolatech-property-pricing", inputs: ["property", "price"] }, { kind: "component", type: PropertySectionComponent, selector: "rolatech-property-section", inputs: ["section", "user", "username"] }, { kind: "component", type: LettingActions, selector: "rolatech-letting-actions", inputs: ["letting"], outputs: ["requestViewing", "offer", "deposit"] }, { kind: "component", type: LettingActionContact, selector: "rolatech-letting-action-contact", inputs: ["phone"], outputs: ["callAgent", "emailAgent"] }, { kind: "component", type: PropertyLocationComponent, selector: "rolatech-property-location", inputs: ["location"] }, { kind: "component", type: PropertyFeaturesComponent, selector: "rolatech-property-features", inputs: ["features"] }, { kind: "component", type: PropertyVideoComponent, selector: "rolatech-property-video", inputs: ["video"] }, { kind: "component", type: PropertyAgentRenderer, selector: "rolatech-property-agent-renderer", inputs: ["name", "avatar", "username", "subtitle"] }] });
3946
4358
  }
3947
4359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImport: i0, type: LettingManageDetail, decorators: [{
3948
4360
  type: Component,
@@ -3958,9 +4370,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0", ngImpor
3958
4370
  LettingActionContact,
3959
4371
  PropertyLocationComponent,
3960
4372
  PropertyFeaturesComponent,
3961
- PropertyVideoTourComponent,
4373
+ PropertyVideoComponent,
3962
4374
  PropertyAgentRenderer
3963
- ], template: "@if (property) {\n<rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <rolatech-property-features [features]=\"property.features\"></rolatech-property-features>\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videoTours) {\n <rolatech-property-video-tour [videoTour]=\"property.videoTours[0]\"></rolatech-property-video-tour>\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 </div>\n </div>\n <div class=\"w-full h-fit md:w-1/3 rounded-md border shadow p-3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n subtitle=\"PrimeCase Agent\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-letting-actions\n [letting]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-letting-action-contact [phone]=\"user.phone\"></rolatech-letting-action-contact>\n }\n </rolatech-letting-actions>\n </div>\n </div>\n </div>\n</rolatech-container>\n}\n" }]
4375
+ ], template: "@if (property) {\n<rolatech-container>\n <div class=\"flex flex-col w-full\">\n <div class=\"py-3\">\n <rolatech-property-media [media]=\"property.media\"></rolatech-property-media>\n </div>\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 <rolatech-property-features [features]=\"property.features\"></rolatech-property-features>\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n @if (property.videos) {\n <rolatech-property-video [video]=\"property.videos[0]\"></rolatech-property-video>\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 </div>\n </div>\n <div class=\"w-full h-fit md:w-1/3 rounded-md border shadow p-3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n subtitle=\"PrimeCase Agent\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-letting-actions\n [letting]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-letting-action-contact [phone]=\"user.phone\"></rolatech-letting-action-contact>\n }\n </rolatech-letting-actions>\n </div>\n </div>\n </div>\n</rolatech-container>\n}\n" }]
3964
4376
  }], ctorParameters: () => [] });
3965
4377
 
3966
4378
  const MY_FORMATS$1 = {
@@ -4309,5 +4721,5 @@ const tenantManageRoutes = [
4309
4721
  * Generated bundle index. Do not edit.
4310
4722
  */
4311
4723
 
4312
- export { AdverseCreditStatus as A, EmploymentStatus as E, PropertyViewingItemComponent as P, ResidencyStatus as R, ViewingTime as V, propertyManageOffersRoutes as a, propertyRoutes as b, propertyManageRoutes as c, propertyManageViewingsRoutes as d, PropertyActionsComponent as e, featureManageRoutes as f, PropertyItemComponent as g, PropertyPricingComponent as h, PropertyPriceType as i, PropertyVideoTourType as j, PropertyStatus as k, lettingManageRoutes as l, PropertyType as m, PropertyInventoryStatus as n, PropertyScope as o, propertyReviewRoutes as p, PropertyViewingStatus as q, PropertyViewerCategory as r, PropertyOfferTimelineStatus as s, tenantManageRoutes as t, PropertyOfferType as u, PropertyOfferStatus as v, PropertyApplicantType as w };
4313
- //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-B4w7sAFv.mjs.map
4724
+ export { AdverseCreditStatus as A, EmploymentStatus as E, PropertyUtil as P, ResidencyStatus as R, ViewingTime as V, PropertyViewType as a, PropertyViewingItemComponent as b, propertyManageOffersRoutes as c, propertyRoutes as d, propertyManageRoutes as e, featureManageRoutes as f, propertyManageViewingsRoutes as g, PropertyActionsComponent as h, PropertyItemComponent as i, PropertyPricingComponent as j, PropertyPriceType as k, lettingManageRoutes as l, PropertyVideoProvider as m, PropertyStatus as n, PropertyType as o, propertyReviewRoutes as p, PropertyInventoryStatus as q, PropertyScope as r, PropertyViewingStatus as s, tenantManageRoutes as t, PropertyViewerCategory as u, PropertyOfferTimelineStatus as v, PropertyOfferType as w, PropertyOfferStatus as x, PropertyApplicantType as y };
4725
+ //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-nGHNbG8j.mjs.map