@rolatech/angular-property 20.2.4-beta.1 → 20.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ 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, ActivatedRoute, Router, RouterLinkActive, RouterOutlet } from '@angular/router';
13
- import { ThumbnailComponent, ImagePlaceholderComponent, Skeleton, ToolbarComponent, AbstractLoading, ImagePreviewDialogComponent, BaseComponent, AcceptDialogComponent, RejectDialogComponent, RichLabelComponent, AngularComponentsModule, ConfirmationDialogComponent, TabsComponent, TabComponent, EmptyComponent, ListComponent, ContainerComponent, SearchBar, SpinnerComponent, MediaListComponent, MediaListItemComponent, InputComponent } from '@rolatech/angular-components';
13
+ import { ThumbnailComponent, ImagePlaceholderComponent, Skeleton, ToolbarComponent, Loading, ImagePreviewDialogComponent, BaseComponent, AcceptDialogComponent, RejectDialogComponent, RichLabelComponent, AngularComponentsModule, ConfirmationDialogComponent, TabsComponent, TabComponent, EmptyComponent, ListComponent, ContainerComponent, SearchBar, SpinnerComponent, MediaListComponent, MediaListItemComponent, InputComponent } from '@rolatech/angular-components';
14
14
  import { PropertyService, FeatureService, PropertyOfferService, InvoiceService, FloorplanService, PropertyHighlightsService, TimeZoneService, PaymentService, 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';
@@ -464,7 +464,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
464
464
  args: ['class.rolatech-property-manage-index']
465
465
  }], paginator: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatPaginator), { isSignal: true }] }] } });
466
466
 
467
- class PropertyMediaComponent extends AbstractLoading {
467
+ class PropertyMediaComponent extends Loading {
468
468
  hasClass = true;
469
469
  disableScrollbar = true;
470
470
  media = input(...(ngDevMode ? [undefined, { debugName: "media" }] : []));
@@ -1686,7 +1686,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1686
1686
  args: [{ selector: 'rolatech-property-pricing', imports: [CommonModule, MatButtonModule, AvailableDatePipe, PricePipe], encapsulation: ViewEncapsulation.None, template: "<div class=\"py-3 flex flex-col gap-3\">\n <div class=\"flex justify-between items-center\">\n @if (market() === 'LETTINGS' && rentFrequency() === 'MONTHLY') {\n <div class=\"text-lg font-bold\" i18n>Rent per month</div>\n }\n @if (market() === 'LETTINGS' && rentFrequency() === 'WEEKLY') {\n <div class=\"text-lg font-bold\" i18n>Rent per week</div>\n }\n @if (market() === 'LETTINGS' && rentFrequency() === 'NOT_SPECIFIED') {\n <div class=\"text-lg font-bold\" i18n>Rent price</div>\n }\n @if (market() === 'SALES') {\n <div class=\"text-lg font-bold\" i18n>Price</div>\n }\n <div class=\"text-xl font-bold\">{{ displayPrice() | price }}</div>\n </div>\n <div class=\"flex justify-between items-center\">\n <div>Available</div>\n <div>{{ property().availableDate | availableDate }}</div>\n </div>\n @if (market() === 'LETTINGS') {\n <div class=\"flex justify-between items-center\">\n <span i18n>Holding Deposit</span>\n <span>{{ holdingDeposit | price }}</span>\n </div>\n <div class=\"flex justify-between items-center font-bold\">\n <span i18n>Security Deposit</span>\n <span>{{ securityDeposit | price }}</span>\n </div>\n }\n</div>\n" }]
1687
1687
  }], propDecorators: { property: [{ type: i0.Input, args: [{ isSignal: true, alias: "property", required: true }] }], price: [{ type: i0.Input, args: [{ isSignal: true, alias: "price", required: false }] }] } });
1688
1688
 
1689
- class PropertyInfoComponent extends AbstractLoading {
1689
+ class PropertyInfoComponent extends Loading {
1690
1690
  property = input.required(...(ngDevMode ? [{ debugName: "property" }] : []));
1691
1691
  wish = output();
1692
1692
  inWishList = input(false, ...(ngDevMode ? [{ debugName: "inWishList" }] : []));
@@ -1965,7 +1965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1965
1965
  args: [{ selector: 'rolatech-property-media-skeleton', imports: [Skeleton], template: "<div class=\"py-3\">\n <div class=\"object-cover aspect-video bg-[--rt-raised-background]\">\n <rolatech-skeleton></rolatech-skeleton>\n </div>\n\n <div class=\"flex overflow-y-hidden overflow-x-scroll scrollbar-hide pb-4\">\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <div class=\"flex flex-row mt-3 mr-3 rounded-md min-w-32 object-cover aspect-video bg-[--rt-raised-background]\">\n <rolatech-skeleton></rolatech-skeleton>\n </div>\n }\n </div>\n</div>\n" }]
1966
1966
  }] });
1967
1967
 
1968
- class PropertyInfoSkeleton extends AbstractLoading {
1968
+ class PropertyInfoSkeleton extends Loading {
1969
1969
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyInfoSkeleton, deps: null, target: i0.ɵɵFactoryTarget.Component });
1970
1970
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.0", type: PropertyInfoSkeleton, isStandalone: true, selector: "rolatech-property-info-skeleton", usesInheritance: true, ngImport: i0, template: "<div>\n <div class=\"flex justify-between items-start gap-3\">\n <!-- title -->\n <rolatech-skeleton class=\"w-[80%] h-8 mb-3\"></rolatech-skeleton>\n <!-- action button -->\n <rolatech-skeleton class=\"w-6 h-6\"></rolatech-skeleton>\n </div>\n <div class=\"font-bold mb-3\">\n <!-- short info -->\n <rolatech-skeleton class=\"w-[50%] h-6\"></rolatech-skeleton>\n </div>\n <!-- description -->\n <rolatech-skeleton class=\"w-full aspect-video\"></rolatech-skeleton>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None });
1971
1971
  }
@@ -1974,7 +1974,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1974
1974
  args: [{ selector: 'rolatech-property-info-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div>\n <div class=\"flex justify-between items-start gap-3\">\n <!-- title -->\n <rolatech-skeleton class=\"w-[80%] h-8 mb-3\"></rolatech-skeleton>\n <!-- action button -->\n <rolatech-skeleton class=\"w-6 h-6\"></rolatech-skeleton>\n </div>\n <div class=\"font-bold mb-3\">\n <!-- short info -->\n <rolatech-skeleton class=\"w-[50%] h-6\"></rolatech-skeleton>\n </div>\n <!-- description -->\n <rolatech-skeleton class=\"w-full aspect-video\"></rolatech-skeleton>\n</div>\n" }]
1975
1975
  }] });
1976
1976
 
1977
- class PropertyAgentSkeleton extends AbstractLoading {
1977
+ class PropertyAgentSkeleton extends Loading {
1978
1978
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyAgentSkeleton, deps: null, target: i0.ɵɵFactoryTarget.Component });
1979
1979
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.0", type: PropertyAgentSkeleton, isStandalone: true, selector: "rolatech-property-agent-skeleton", usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col\">\n <div class=\"flex flex-row items-center pb-3\">\n <div class=\"flex mr-3 gap-2 items-center w-full\">\n <!-- avatar -->\n <rolatech-skeleton class=\"w-14 h-14 rounded-full\"></rolatech-skeleton>\n <rolatech-skeleton class=\"flex-1 h-6\"></rolatech-skeleton>\n </div>\n </div>\n <div class=\"text-sm opacity-70\">\n <rolatech-skeleton class=\"w-full h-6\"></rolatech-skeleton>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None });
1980
1980
  }
@@ -2001,19 +2001,27 @@ class PropertyDetailsComponent extends BaseComponent {
2001
2001
  selectedOption;
2002
2002
  variants = [];
2003
2003
  features = [];
2004
+ allVideos = signal([], ...(ngDevMode ? [{ debugName: "allVideos" }] : []));
2004
2005
  highlights = [];
2005
2006
  floorplans = [];
2006
2007
  epc;
2007
2008
  timezone = signal('', ...(ngDevMode ? [{ debugName: "timezone" }] : []));
2008
- get videos() {
2009
- const p = this.property(); // read signal once
2010
- if (!p || !p.videos)
2009
+ // get videos(): PropertyVideo[] {
2010
+ // const v = this.allVideos; // read signal once
2011
+ // if (!v) return [];
2012
+ // const tz = this.timezone();
2013
+ // const provider = tz === 'Asia/Shanghai' ? PropertyVideoProvider.COS : PropertyVideoProvider.YOUTUBE;
2014
+ // console.log(v);
2015
+ // return v.filter((v) => v.provider === provider);
2016
+ // }
2017
+ videos = computed(() => {
2018
+ const v = this.allVideos(); // read signal once
2019
+ if (!v)
2011
2020
  return [];
2012
2021
  const tz = this.timezone();
2013
2022
  const provider = tz === 'Asia/Shanghai' ? PropertyVideoProvider.COS : PropertyVideoProvider.YOUTUBE;
2014
- return p.videos.filter((v) => v.provider === provider);
2015
- }
2016
- // results: any[] = [];
2023
+ return v.filter((v) => v.provider === provider);
2024
+ }, ...(ngDevMode ? [{ debugName: "videos" }] : []));
2017
2025
  constructor() {
2018
2026
  super();
2019
2027
  effect(() => {
@@ -2024,6 +2032,7 @@ class PropertyDetailsComponent extends BaseComponent {
2024
2032
  }
2025
2033
  ngOnInit() {
2026
2034
  this.getProperty();
2035
+ this.getVideos();
2027
2036
  this.getFeatures();
2028
2037
  this.getHighlights();
2029
2038
  this.getFloorplans();
@@ -2056,13 +2065,11 @@ class PropertyDetailsComponent extends BaseComponent {
2056
2065
  },
2057
2066
  });
2058
2067
  }
2059
- findVariant(optionId, valueId) {
2060
- return this.variants.find((item) => {
2061
- const exist = item.options.some((i) => i.option.id === optionId && i.value.id === valueId);
2062
- if (exist) {
2063
- return item;
2064
- }
2065
- return null;
2068
+ getVideos() {
2069
+ this.propertyService.getVideos(this.id).subscribe({
2070
+ next: (res) => {
2071
+ this.allVideos.set(res.data);
2072
+ },
2066
2073
  });
2067
2074
  }
2068
2075
  findUserBaseInfo(userId) {
@@ -2150,7 +2157,7 @@ class PropertyDetailsComponent extends BaseComponent {
2150
2157
  }
2151
2158
  }
2152
2159
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2153
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyDetailsComponent, isStandalone: true, selector: "rolatech-property-details", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col w-full\">\n @if (loading()) {\n <rolatech-property-media-skeleton></rolatech-property-media-skeleton>\n\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <rolatech-property-info-skeleton class=\"w-full md:w-2/3\"></rolatech-property-info-skeleton>\n <rolatech-property-agent-skeleton class=\"w-full md:w-1/3\"></rolatech-property-agent-skeleton>\n </div>\n } @else {\n @if (property(); as property) {\n <rolatech-property-media [media]=\"property.media ? property.media : []\"></rolatech-property-media>\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <div class=\"w-full md:w-2/3\">\n <rolatech-property-info [property]=\"property\" (wish)=\"onWish($event)\" [inWishList]=\"inWishList\">\n </rolatech-property-info>\n <!-- <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer> -->\n @if (features.length > 0) {\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n }\n @if (highlights.length > 0) {\n <rolatech-property-highlights [highlights]=\"highlights\"></rolatech-property-highlights>\n }\n @if (floorplans.length > 0) {\n <rolatech-property-floorplan [floorplans]=\"floorplans\"></rolatech-property-floorplan>\n }\n @if (property.location) {\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n }\n @if (videos && videos.length > 0) {\n @for (item of videos; track $index) {\n <rolatech-property-video [video]=\"item\"></rolatech-property-video>\n }\n }\n @if (epc && epc.currentScore) {\n <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc>\n }\n <div class=\"flex flex-col\">\n <div class=\"text-2xl font-bold pt-3\" i18n>Sections</div>\n @for (section of property.sections; track $index) {\n <rolatech-property-section [section]=\"section\"></rolatech-property-section>\n }\n <rolatech-comments [itemId]=\"property.id\"></rolatech-comments>\n </div>\n </div>\n <div class=\"w-full md:w-1/3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-property-actions\n [property]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-property-action-contact [email]=\"user.email\" [phone]=\"user.phone\"></rolatech-property-action-contact>\n }\n </rolatech-property-actions>\n </div>\n </div>\n }\n }\n </div>\n</rolatech-container>\n", 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: 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"] }, { kind: "component", type: PropertyFloorplan, selector: "rolatech-property-floorplan", inputs: ["floorplans"] }, { kind: "component", type: PropertyMediaSkeleton, selector: "rolatech-property-media-skeleton" }, { kind: "component", type: PropertyInfoSkeleton, selector: "rolatech-property-info-skeleton" }, { kind: "component", type: PropertyAgentSkeleton, selector: "rolatech-property-agent-skeleton" }] });
2160
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyDetailsComponent, isStandalone: true, selector: "rolatech-property-details", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <div class=\"flex flex-col w-full\">\n @if (loading()) {\n <rolatech-property-media-skeleton></rolatech-property-media-skeleton>\n\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <rolatech-property-info-skeleton class=\"w-full md:w-2/3\"></rolatech-property-info-skeleton>\n <rolatech-property-agent-skeleton class=\"w-full md:w-1/3\"></rolatech-property-agent-skeleton>\n </div>\n } @else {\n @if (property(); as property) {\n <rolatech-property-media [media]=\"property.media ? property.media : []\"></rolatech-property-media>\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <div class=\"w-full md:w-2/3\">\n <rolatech-property-info [property]=\"property\" (wish)=\"onWish($event)\" [inWishList]=\"inWishList\">\n </rolatech-property-info>\n <!-- <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer> -->\n @if (features.length > 0) {\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n }\n @if (highlights.length > 0) {\n <rolatech-property-highlights [highlights]=\"highlights\"></rolatech-property-highlights>\n }\n @if (floorplans.length > 0) {\n <rolatech-property-floorplan [floorplans]=\"floorplans\"></rolatech-property-floorplan>\n }\n @if (property.location) {\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n }\n @if (videos() && videos().length > 0) {\n @for (item of videos(); track $index) {\n <rolatech-property-video [video]=\"item\"></rolatech-property-video>\n }\n }\n @if (epc && epc.currentScore) {\n <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc>\n }\n <div class=\"flex flex-col\">\n <div class=\"text-2xl font-bold pt-3\" i18n>Sections</div>\n @for (section of property.sections; track $index) {\n <rolatech-property-section [section]=\"section\"></rolatech-property-section>\n }\n <rolatech-comments [itemId]=\"property.id\"></rolatech-comments>\n </div>\n </div>\n <div class=\"w-full md:w-1/3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-property-actions\n [property]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-property-action-contact [email]=\"user.email\" [phone]=\"user.phone\"></rolatech-property-action-contact>\n }\n </rolatech-property-actions>\n </div>\n </div>\n }\n }\n </div>\n</rolatech-container>\n", 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: 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"] }, { kind: "component", type: PropertyFloorplan, selector: "rolatech-property-floorplan", inputs: ["floorplans"] }, { kind: "component", type: PropertyMediaSkeleton, selector: "rolatech-property-media-skeleton" }, { kind: "component", type: PropertyInfoSkeleton, selector: "rolatech-property-info-skeleton" }, { kind: "component", type: PropertyAgentSkeleton, selector: "rolatech-property-agent-skeleton" }] });
2154
2161
  }
2155
2162
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyDetailsComponent, decorators: [{
2156
2163
  type: Component,
@@ -2175,7 +2182,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
2175
2182
  PropertyMediaSkeleton,
2176
2183
  PropertyInfoSkeleton,
2177
2184
  PropertyAgentSkeleton,
2178
- ], template: "<rolatech-container>\n <div class=\"flex flex-col w-full\">\n @if (loading()) {\n <rolatech-property-media-skeleton></rolatech-property-media-skeleton>\n\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <rolatech-property-info-skeleton class=\"w-full md:w-2/3\"></rolatech-property-info-skeleton>\n <rolatech-property-agent-skeleton class=\"w-full md:w-1/3\"></rolatech-property-agent-skeleton>\n </div>\n } @else {\n @if (property(); as property) {\n <rolatech-property-media [media]=\"property.media ? property.media : []\"></rolatech-property-media>\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <div class=\"w-full md:w-2/3\">\n <rolatech-property-info [property]=\"property\" (wish)=\"onWish($event)\" [inWishList]=\"inWishList\">\n </rolatech-property-info>\n <!-- <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer> -->\n @if (features.length > 0) {\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n }\n @if (highlights.length > 0) {\n <rolatech-property-highlights [highlights]=\"highlights\"></rolatech-property-highlights>\n }\n @if (floorplans.length > 0) {\n <rolatech-property-floorplan [floorplans]=\"floorplans\"></rolatech-property-floorplan>\n }\n @if (property.location) {\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n }\n @if (videos && videos.length > 0) {\n @for (item of videos; track $index) {\n <rolatech-property-video [video]=\"item\"></rolatech-property-video>\n }\n }\n @if (epc && epc.currentScore) {\n <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc>\n }\n <div class=\"flex flex-col\">\n <div class=\"text-2xl font-bold pt-3\" i18n>Sections</div>\n @for (section of property.sections; track $index) {\n <rolatech-property-section [section]=\"section\"></rolatech-property-section>\n }\n <rolatech-comments [itemId]=\"property.id\"></rolatech-comments>\n </div>\n </div>\n <div class=\"w-full md:w-1/3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-property-actions\n [property]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-property-action-contact [email]=\"user.email\" [phone]=\"user.phone\"></rolatech-property-action-contact>\n }\n </rolatech-property-actions>\n </div>\n </div>\n }\n }\n </div>\n</rolatech-container>\n" }]
2185
+ ], template: "<rolatech-container>\n <div class=\"flex flex-col w-full\">\n @if (loading()) {\n <rolatech-property-media-skeleton></rolatech-property-media-skeleton>\n\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <rolatech-property-info-skeleton class=\"w-full md:w-2/3\"></rolatech-property-info-skeleton>\n <rolatech-property-agent-skeleton class=\"w-full md:w-1/3\"></rolatech-property-agent-skeleton>\n </div>\n } @else {\n @if (property(); as property) {\n <rolatech-property-media [media]=\"property.media ? property.media : []\"></rolatech-property-media>\n <div class=\"flex flex-col md:flex-row gap-3 w-full\">\n <div class=\"w-full md:w-2/3\">\n <rolatech-property-info [property]=\"property\" (wish)=\"onWish($event)\" [inWishList]=\"inWishList\">\n </rolatech-property-info>\n <!-- <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer> -->\n @if (features.length > 0) {\n <rolatech-property-features [features]=\"features\"></rolatech-property-features>\n }\n @if (highlights.length > 0) {\n <rolatech-property-highlights [highlights]=\"highlights\"></rolatech-property-highlights>\n }\n @if (floorplans.length > 0) {\n <rolatech-property-floorplan [floorplans]=\"floorplans\"></rolatech-property-floorplan>\n }\n @if (property.location) {\n <rolatech-property-location [location]=\"property.location\"></rolatech-property-location>\n }\n @if (videos() && videos().length > 0) {\n @for (item of videos(); track $index) {\n <rolatech-property-video [video]=\"item\"></rolatech-property-video>\n }\n }\n @if (epc && epc.currentScore) {\n <rolatech-property-epc [epc]=\"epc\"></rolatech-property-epc>\n }\n <div class=\"flex flex-col\">\n <div class=\"text-2xl font-bold pt-3\" i18n>Sections</div>\n @for (section of property.sections; track $index) {\n <rolatech-property-section [section]=\"section\"></rolatech-property-section>\n }\n <rolatech-comments [itemId]=\"property.id\"></rolatech-comments>\n </div>\n </div>\n <div class=\"w-full md:w-1/3\">\n <rolatech-property-agent-renderer\n [name]=\"fullname\"\n [avatar]=\"user?.avatar\"\n [username]=\"username\"\n ></rolatech-property-agent-renderer>\n <rolatech-property-pricing (wish)=\"onWish($event)\" [property]=\"property\"></rolatech-property-pricing>\n <rolatech-property-actions\n [property]=\"property\"\n (offer)=\"onOffer($event)\"\n (deposit)=\"onOffer($event)\"\n (requestViewing)=\"onRequestViewing($event)\"\n >\n @if (user) {\n <rolatech-property-action-contact [email]=\"user.email\" [phone]=\"user.phone\"></rolatech-property-action-contact>\n }\n </rolatech-property-actions>\n </div>\n </div>\n }\n }\n </div>\n</rolatech-container>\n" }]
2179
2186
  }], ctorParameters: () => [] });
2180
2187
 
2181
2188
  const MY_FORMATS$5 = {
@@ -2598,20 +2605,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
2598
2605
  ], encapsulation: ViewEncapsulation.None, template: "<rolatech-container>\n <rolatech-toolbar title=\"Offers\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n @if (loading) {\n <div class=\"divide-y divide-[--rt-10-percent-layer] flex flex-col\">\n @for (row of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; track row) {\n <rolatech-offer-item-skeleton></rolatech-offer-item-skeleton>\n }\n </div>\n } @else {\n <rolatech-list>\n @if (offers() && offers().length > 0) {\n @for (item of offers(); track item) {\n <rolatech-property-offer-item [routerLink]=\"['./', item.id]\" [offer]=\"item\"></rolatech-property-offer-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\n }\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n</rolatech-container>\n" }]
2599
2606
  }] });
2600
2607
 
2601
- class PropertyViewingItemComponent {
2608
+ class PropertyViewingItemComponent extends Loading {
2602
2609
  viewing = input.required(...(ngDevMode ? [{ debugName: "viewing" }] : []));
2603
2610
  status = PropertyViewingStatus;
2604
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyViewingItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2605
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyViewingItemComponent, isStandalone: true, selector: "rolatech-property-viewing-item", inputs: { viewing: { classPropertyName: "viewing", publicName: "viewing", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <a class=\"flex items-center gap-3\">\n <span>Viewing ID: {{ viewing().id }}</span>\n </a>\n <span class=\"font-medium text-sm\">{{ status[viewing().status] }}</span>\n </div>\n @if (viewing().item) {\n <div class=\"flex flex-col overflow-x-scroll scrollbar-hide\">\n <div class=\"flex flex-row py-2\">\n @if (viewing().item.media) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"viewing().item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing().item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing().item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing().item.price | price }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
2611
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyViewingItemComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2612
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyViewingItemComponent, isStandalone: true, selector: "rolatech-property-viewing-item", inputs: { viewing: { classPropertyName: "viewing", publicName: "viewing", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "@if (loading()) {\n <div class=\"flex flex-col p-3\">\n <div class=\"flex justify-between w-full mb-2\">\n <rolatech-skeleton class=\"w-52 h-5\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-5\"></rolatech-skeleton>\n </div>\n <div class=\"flex flex-row py-2\">\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-skeleton class=\"w-full h-full\"></rolatech-skeleton>\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <rolatech-skeleton class=\"w-40 h-4\"></rolatech-skeleton>\n <div class=\"inline-flex gap-1 mt-2\">\n <rolatech-skeleton class=\"w-20 h-4\"></rolatech-skeleton>\n </div>\n </div>\n <div class=\"text-right\">\n <rolatech-skeleton class=\"w-8 h-4\"></rolatech-skeleton>\n </div>\n </div>\n </div>\n </div>\n </div>\n} @else {\n <div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <a class=\"flex items-center gap-3\">\n <span>Viewing ID: {{ viewing().id }}</span>\n </a>\n <span class=\"font-medium text-sm\">{{ status[viewing().status] }}</span>\n </div>\n @if (viewing().item) {\n <div class=\"flex flex-col overflow-x-scroll scrollbar-hide\">\n <div class=\"flex flex-row py-2\">\n @if (viewing().item.media) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"viewing().item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div class=\"font-bold\">{{ viewing().item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing().item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing().item.price | price }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n}\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: ImagePlaceholderComponent, selector: "rolatech-image-placeholder", inputs: ["ratio"] }, { kind: "component", type: Skeleton, selector: "rolatech-skeleton" }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
2606
2613
  }
2607
2614
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyViewingItemComponent, decorators: [{
2608
2615
  type: Component,
2609
- args: [{ selector: 'rolatech-property-viewing-item', imports: [MatIconModule, ThumbnailComponent, ImagePlaceholderComponent, PricePipe], template: "<div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <a class=\"flex items-center gap-3\">\n <span>Viewing ID: {{ viewing().id }}</span>\n </a>\n <span class=\"font-medium text-sm\">{{ status[viewing().status] }}</span>\n </div>\n @if (viewing().item) {\n <div class=\"flex flex-col overflow-x-scroll scrollbar-hide\">\n <div class=\"flex flex-row py-2\">\n @if (viewing().item.media) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"viewing().item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing().item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing().item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing().item.price | price }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n</div>\n" }]
2616
+ args: [{ selector: 'rolatech-property-viewing-item', imports: [MatIconModule, ThumbnailComponent, ImagePlaceholderComponent, Skeleton, PricePipe], template: "@if (loading()) {\n <div class=\"flex flex-col p-3\">\n <div class=\"flex justify-between w-full mb-2\">\n <rolatech-skeleton class=\"w-52 h-5\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-5\"></rolatech-skeleton>\n </div>\n <div class=\"flex flex-row py-2\">\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-skeleton class=\"w-full h-full\"></rolatech-skeleton>\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <rolatech-skeleton class=\"w-40 h-4\"></rolatech-skeleton>\n <div class=\"inline-flex gap-1 mt-2\">\n <rolatech-skeleton class=\"w-20 h-4\"></rolatech-skeleton>\n </div>\n </div>\n <div class=\"text-right\">\n <rolatech-skeleton class=\"w-8 h-4\"></rolatech-skeleton>\n </div>\n </div>\n </div>\n </div>\n </div>\n} @else {\n <div class=\"flex flex-col hover:bg-[--rt-raised-background] cursor-pointer p-3\">\n <div>\n <div class=\"flex justify-between w-full mb-2\">\n <a class=\"flex items-center gap-3\">\n <span>Viewing ID: {{ viewing().id }}</span>\n </a>\n <span class=\"font-medium text-sm\">{{ status[viewing().status] }}</span>\n </div>\n @if (viewing().item) {\n <div class=\"flex flex-col overflow-x-scroll scrollbar-hide\">\n <div class=\"flex flex-row py-2\">\n @if (viewing().item.media) {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"viewing().item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n } @else {\n <div class=\"min-w-24 w-36 object-cover aspect-video rounded-lg mr-3\">\n <rolatech-image-placeholder></rolatech-image-placeholder>\n </div>\n }\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div class=\"font-bold\">{{ viewing().item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing().item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing().item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing().item.price | price }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n}\n" }]
2610
2617
  }], propDecorators: { viewing: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewing", required: true }] }] } });
2611
2618
 
2612
2619
  class PropertyViewingIndexComponent extends BaseComponent {
2613
2620
  propertyService = inject(PropertyService);
2614
- viewings;
2615
2621
  viewerCategory = PropertyViewerCategory;
2616
2622
  tenantCategory = PropertyApplicantType;
2617
2623
  viewingStatus = PropertyViewingStatus;
@@ -2638,53 +2644,66 @@ class PropertyViewingIndexComponent extends BaseComponent {
2638
2644
  status: 'cancelled',
2639
2645
  },
2640
2646
  ];
2647
+ loading = false;
2648
+ viewings = signal([], ...(ngDevMode ? [{ debugName: "viewings" }] : []));
2649
+ meta;
2650
+ pageEvent;
2651
+ length = 100;
2652
+ pageSize = 15;
2653
+ pageSizeOptions = [5, 10, 25, 100];
2654
+ pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
2641
2655
  ngOnInit() {
2642
- this.route.queryParams.subscribe(({ status }) => {
2643
- this.select = this.links.findIndex((item) => item.status === status);
2656
+ const sub = this.route.queryParamMap
2657
+ .pipe(map((p) => {
2658
+ const page = p.get('page') ? Number(p.get('page')) : 1;
2659
+ this.pageIndex.set(Math.max(page - 1, 0));
2660
+ const status = p.get('status') || undefined;
2661
+ let filter = '';
2644
2662
  if (status) {
2645
- this.filterOptions.status = status.toUpperCase();
2646
- }
2647
- else {
2648
- delete this.filterOptions.status;
2663
+ this.select = this.links.findIndex((item) => item.status === status);
2664
+ filter = `status:${status?.toUpperCase()}`;
2649
2665
  }
2650
- this.findViewings();
2651
- });
2652
- }
2653
- findViewings() {
2654
- const options = {
2655
- sort: 'updatedAt desc',
2656
- };
2657
- const filterString = this.convertFilterOptions(this.filterOptions);
2658
- if (filterString) {
2659
- options['filter'] = filterString;
2660
- }
2661
- this.propertyService.findViewingsByUser(options).subscribe({
2666
+ return {
2667
+ page,
2668
+ filter,
2669
+ limit: p.get('limit') ? Number(p.get('limit')) : 15,
2670
+ sort: p.get('sort') || undefined,
2671
+ };
2672
+ }),
2673
+ // Cheap deep compare via JSON to avoid spam calls when nothing changed
2674
+ map((o) => JSON.stringify(o)), distinctUntilChanged(), map((s) => JSON.parse(s)), switchMap((params) => {
2675
+ this.loading = true;
2676
+ params['sort'] = 'updatedAt desc';
2677
+ return this.propertyService.findViewingsByUser(params).pipe(finalize(() => (this.loading = false)));
2678
+ }))
2679
+ .subscribe({
2662
2680
  next: (res) => {
2663
- this.viewings = res.data;
2681
+ this.viewings.set(res.data);
2682
+ this.meta = res.meta;
2683
+ this.length = res.meta.pagination.count;
2684
+ this.loading = false;
2685
+ },
2686
+ error: () => {
2687
+ this.viewings.set([]);
2688
+ this.length = 0;
2689
+ this.loading = false;
2664
2690
  },
2665
2691
  });
2666
- }
2667
- findProperties(ids) { }
2668
- resetFilter() {
2669
- this.filterOptions = {
2670
- type: '',
2671
- };
2672
- this.filter = false;
2673
- this.findViewings();
2692
+ // // auto-unsubscribe on destroy
2693
+ this.destroyRef.onDestroy(() => sub.unsubscribe());
2674
2694
  }
2675
2695
  statusCompareFn(t1, t2) {
2676
2696
  return t1 === t2;
2677
2697
  }
2678
- convertFilterOptions(jsonObj) {
2679
- return Object.entries(jsonObj)
2680
- .filter(([key, value]) => value !== '' && value !== undefined)
2681
- .map(([key, value]) => {
2682
- return `${key}:${value}`;
2683
- })
2684
- .join(',');
2698
+ onPage(e) {
2699
+ this.router.navigate([], {
2700
+ queryParams: { page: e.pageIndex + 1, limit: e.pageSize },
2701
+ queryParamsHandling: 'merge',
2702
+ replaceUrl: true, // optional: avoid stacking history on every page click
2703
+ });
2685
2704
  }
2686
2705
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyViewingIndexComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2687
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyViewingIndexComponent, isStandalone: true, selector: "rolatech-property-viewing-index", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item [routerLink]=\"['./', item.id]\" [viewing]=\"item\"></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\n</rolatech-container>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ListComponent, selector: "rolatech-list" }, { kind: "component", type: EmptyComponent, selector: "rolatech-empty" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }] });
2706
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyViewingIndexComponent, isStandalone: true, selector: "rolatech-property-viewing-index", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings(); as viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n</rolatech-container>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ListComponent, selector: "rolatech-list" }, { kind: "component", type: EmptyComponent, selector: "rolatech-empty" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$2.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }] });
2688
2707
  }
2689
2708
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyViewingIndexComponent, decorators: [{
2690
2709
  type: Component,
@@ -2707,7 +2726,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
2707
2726
  MatSelectModule,
2708
2727
  MatButtonModule,
2709
2728
  PropertyViewingItemComponent,
2710
- ], template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item [routerLink]=\"['./', item.id]\" [viewing]=\"item\"></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\n</rolatech-container>\n" }]
2729
+ MatPaginatorModule,
2730
+ ], template: "<rolatech-container>\n <rolatech-toolbar title=\"Viewings\" large> </rolatech-toolbar>\n <rolatech-tabs [select]=\"select\">\n @for (item of links; track item) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (viewings(); as viewings) {\n @for (item of viewings; track item) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n</rolatech-container>\n" }]
2711
2731
  }] });
2712
2732
 
2713
2733
  class DuePipe {
@@ -2904,7 +2924,6 @@ class PropertyViewingDetailComponent extends BaseComponent {
2904
2924
  loadingTimeline = false;
2905
2925
  status = PropertyViewingStatus;
2906
2926
  env = inject(APP_CONFIG);
2907
- property;
2908
2927
  async ngOnInit() {
2909
2928
  this.route.params.subscribe((params) => {
2910
2929
  const id = params['id'];
@@ -2915,19 +2934,10 @@ class PropertyViewingDetailComponent extends BaseComponent {
2915
2934
  this.propertyService.getViewing(id).subscribe({
2916
2935
  next: (res) => {
2917
2936
  this.viewing = res.data;
2918
- this.getProperty(this.viewing.propertyId);
2919
2937
  // this.findAgentInfo();
2920
2938
  },
2921
2939
  });
2922
2940
  }
2923
- getProperty(propertyId) {
2924
- this.propertyService.get(propertyId).subscribe({
2925
- next: (res) => {
2926
- this.property = res.data;
2927
- this.getAgentPublicInfo(this.property.agentId);
2928
- },
2929
- });
2930
- }
2931
2941
  getAgentPublicInfo(agentId) {
2932
2942
  this.authUserService.getPublicUserInfo(agentId).subscribe({
2933
2943
  next: (res) => {
@@ -2947,7 +2957,7 @@ class PropertyViewingDetailComponent extends BaseComponent {
2947
2957
  });
2948
2958
  }
2949
2959
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyViewingDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2950
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", usesInheritance: true, ngImport: i0, template: "@if (viewing) {\n <rolatech-container>\n <rolatech-toolbar [title]=\"status[viewing.status]\" large link=\"../\"> </rolatech-toolbar>\n <div>\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n @if (property) {\n <div\n class=\"flex items-center py-2 cursor-pointer hover:bg-[--rt-raised-background]\"\n [routerLink]=\"['../../', property.id]\"\n >\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"property.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ property.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ property.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ property.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ property.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ property.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) {\n @if (item.date === viewing.viewingDate && item.time === viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n }\n }\n </div>\n }\n </div>\n <!-- safe area -->\n <div class=\"pb-16 sm:pb-3\"></div>\n <div class=\"\">\n <div class=\"flex items-center justify-end\">\n @if (viewing.status.toString() !== 'CANCELLED') {\n <button mat-flat-button class=\"min-h-11\" (click)=\"cancel()\" i18n>Cancel</button>\n }\n </div>\n </div>\n </div>\n </rolatech-container>\n}\n@if (info) {\n <div\n [ngClass]=\"loadingTimeline ? 'translate-x-full' : 'translate-none'\"\n class=\"fixed top-0 right-0 z-[1001] h-screen p-4 overflow-y-auto transition-transform bg-[--rt-raised-background] w-80 sm:w-[300px] shadow-xl\"\n >\n <div class=\"flex justify-between items-center sm:p-4\">\n <div class=\"text-xl font-bold\" i18n>Status</div>\n <button mat-icon-button (click)=\"info = !info\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n}\n\n<div\n (click)=\"info = !info\"\n [ngClass]=\"info ? 'visible' : 'invisible'\"\n class=\"bg-[--rt-10-percent-layer] fixed inset-0 z-[1000]\"\n></div>\n", styles: [""], dependencies: [{ 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: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: RichLabelComponent, selector: "rolatech-rich-label", inputs: ["label", "title"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
2960
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyViewingDetailComponent, isStandalone: true, selector: "rolatech-property-viewing-detail", usesInheritance: true, ngImport: i0, template: "@if (viewing) {\n <rolatech-container>\n <rolatech-toolbar [title]=\"status[viewing.status]\" large link=\"../\"> </rolatech-toolbar>\n <div>\n @if (viewing.item) {\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n <div\n class=\"flex items-center py-2 cursor-pointer hover:bg-[--rt-raised-background]\"\n [routerLink]=\"['../../', viewing.item.propertyId]\"\n >\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"viewing.item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing.item.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n </div>\n }\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) {\n @if (item.date === viewing.viewingDate && item.time === viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n }\n }\n </div>\n }\n </div>\n <!-- safe area -->\n <div class=\"pb-16 sm:pb-3\"></div>\n <div class=\"\">\n <div class=\"flex items-center justify-end\">\n @if (viewing.status.toString() !== 'CANCELLED') {\n <button mat-flat-button class=\"min-h-11\" (click)=\"cancel()\" i18n>Cancel</button>\n }\n </div>\n </div>\n </div>\n </rolatech-container>\n}\n@if (info) {\n <div\n [ngClass]=\"loadingTimeline ? 'translate-x-full' : 'translate-none'\"\n class=\"fixed top-0 right-0 z-[1001] h-screen p-4 overflow-y-auto transition-transform bg-[--rt-raised-background] w-80 sm:w-[300px] shadow-xl\"\n >\n <div class=\"flex justify-between items-center sm:p-4\">\n <div class=\"text-xl font-bold\" i18n>Status</div>\n <button mat-icon-button (click)=\"info = !info\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n}\n\n<div\n (click)=\"info = !info\"\n [ngClass]=\"info ? 'visible' : 'invisible'\"\n class=\"bg-[--rt-10-percent-layer] fixed inset-0 z-[1000]\"\n></div>\n", styles: [""], dependencies: [{ 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: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: RichLabelComponent, selector: "rolatech-rich-label", inputs: ["label", "title"] }, { kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: PricePipe, name: "price" }], deferBlockDependencies: [() => [ThumbnailComponent]] });
2951
2961
  }
2952
2962
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyViewingDetailComponent, decorators: [{
2953
2963
  type: Component,
@@ -2961,7 +2971,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
2961
2971
  ToolbarComponent,
2962
2972
  RouterLink,
2963
2973
  PricePipe,
2964
- ], template: "@if (viewing) {\n <rolatech-container>\n <rolatech-toolbar [title]=\"status[viewing.status]\" large link=\"../\"> </rolatech-toolbar>\n <div>\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n @if (property) {\n <div\n class=\"flex items-center py-2 cursor-pointer hover:bg-[--rt-raised-background]\"\n [routerLink]=\"['../../', property.id]\"\n >\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"property.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ property.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ property.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ property.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ property.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ property.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n }\n </div>\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) {\n @if (item.date === viewing.viewingDate && item.time === viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n }\n }\n </div>\n }\n </div>\n <!-- safe area -->\n <div class=\"pb-16 sm:pb-3\"></div>\n <div class=\"\">\n <div class=\"flex items-center justify-end\">\n @if (viewing.status.toString() !== 'CANCELLED') {\n <button mat-flat-button class=\"min-h-11\" (click)=\"cancel()\" i18n>Cancel</button>\n }\n </div>\n </div>\n </div>\n </rolatech-container>\n}\n@if (info) {\n <div\n [ngClass]=\"loadingTimeline ? 'translate-x-full' : 'translate-none'\"\n class=\"fixed top-0 right-0 z-[1001] h-screen p-4 overflow-y-auto transition-transform bg-[--rt-raised-background] w-80 sm:w-[300px] shadow-xl\"\n >\n <div class=\"flex justify-between items-center sm:p-4\">\n <div class=\"text-xl font-bold\" i18n>Status</div>\n <button mat-icon-button (click)=\"info = !info\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n}\n\n<div\n (click)=\"info = !info\"\n [ngClass]=\"info ? 'visible' : 'invisible'\"\n class=\"bg-[--rt-10-percent-layer] fixed inset-0 z-[1000]\"\n></div>\n" }]
2974
+ ], template: "@if (viewing) {\n <rolatech-container>\n <rolatech-toolbar [title]=\"status[viewing.status]\" large link=\"../\"> </rolatech-toolbar>\n <div>\n @if (viewing.item) {\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Property details</div>\n <hr class=\"mb-2\" />\n <div\n class=\"flex items-center py-2 cursor-pointer hover:bg-[--rt-raised-background]\"\n [routerLink]=\"['../../', viewing.item.propertyId]\"\n >\n <div class=\"min-w-24 w-24 object-cover aspect-video rounded-md mr-3\">\n @defer {\n <rolatech-thumbnail [src]=\"viewing.item.media[0].url\" size=\"medium\" mode=\"clip\"> </rolatech-thumbnail>\n } @placeholder {\n <div class=\"bg-[--rt-raised-background] h-full w-full object-cover aspect-video rounded-lg\"></div>\n }\n </div>\n <div class=\"flex w-full justify-between\">\n <div class=\"flex justify-between w-full\">\n <div class=\"flex flex-col\">\n <div>{{ viewing.item.title }}</div>\n <div class=\"inline-flex gap-1 mt-2\">\n <div>\n <span class=\"mr-1\">{{ viewing.item.bedrooms }}</span>\n <span i18n>Bedrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.bathrooms }}</span>\n <span i18n>Bathrooms</span>\n </div>\n <div>\n <span class=\"mr-1\">{{ viewing.item.receptions }}</span>\n <span i18n>Receptions</span>\n </div>\n </div>\n </div>\n <div class=\"text-right\">\n <div class=\"text-sm\">{{ viewing.item.price | price }}</div>\n </div>\n </div>\n </div>\n <div class=\"hidden md:flex flex-col px-3\"></div>\n </div>\n </div>\n }\n\n <div>\n <div class=\"text-lg font-bold py-2\" i18n>Proposed times</div>\n <hr class=\"mb-2\" />\n @for (item of viewing.proposedSlots; track $index) {\n <div class=\"flex flex-row items-center gap-3 py-3\">\n <rolatech-rich-label label=\"Date\" [title]=\"item.date\"></rolatech-rich-label>\n <rolatech-rich-label label=\"Time\" [title]=\"item.time\"></rolatech-rich-label>\n @if (viewing.viewingDate && viewing.viewingTime) {\n @if (item.date === viewing.viewingDate && item.time === viewing.viewingTime) {\n <div class=\"ml-3\"><button mat-flat-button i18n disabled=\"\">Confirmed</button></div>\n }\n }\n </div>\n }\n </div>\n <!-- safe area -->\n <div class=\"pb-16 sm:pb-3\"></div>\n <div class=\"\">\n <div class=\"flex items-center justify-end\">\n @if (viewing.status.toString() !== 'CANCELLED') {\n <button mat-flat-button class=\"min-h-11\" (click)=\"cancel()\" i18n>Cancel</button>\n }\n </div>\n </div>\n </div>\n </rolatech-container>\n}\n@if (info) {\n <div\n [ngClass]=\"loadingTimeline ? 'translate-x-full' : 'translate-none'\"\n class=\"fixed top-0 right-0 z-[1001] h-screen p-4 overflow-y-auto transition-transform bg-[--rt-raised-background] w-80 sm:w-[300px] shadow-xl\"\n >\n <div class=\"flex justify-between items-center sm:p-4\">\n <div class=\"text-xl font-bold\" i18n>Status</div>\n <button mat-icon-button (click)=\"info = !info\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n}\n\n<div\n (click)=\"info = !info\"\n [ngClass]=\"info ? 'visible' : 'invisible'\"\n class=\"bg-[--rt-10-percent-layer] fixed inset-0 z-[1000]\"\n></div>\n" }]
2965
2975
  }] });
2966
2976
 
2967
2977
  class PropertyWishlistComponent extends BaseComponent {
@@ -3009,7 +3019,7 @@ const propertyRoutes = [
3009
3019
  children: [
3010
3020
  {
3011
3021
  path: '',
3012
- loadComponent: () => import('./rolatech-angular-property-property-index.component-DgcQjaUr.mjs').then((x) => x.PropertyIndexComponent),
3022
+ loadComponent: () => import('./rolatech-angular-property-property-index.component-DepoNMLG.mjs').then((x) => x.PropertyIndexComponent),
3013
3023
  },
3014
3024
  ],
3015
3025
  },
@@ -4923,7 +4933,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
4923
4933
  const propertyManageViewingsRoutes = [
4924
4934
  {
4925
4935
  path: '',
4926
- loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-1MXQYu4A.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
4936
+ loadComponent: () => import('./rolatech-angular-property-property-manage-viewings-index.component-BmG4wx99.mjs').then((x) => x.PropertyManageViewingsIndexComponent),
4927
4937
  },
4928
4938
  {
4929
4939
  path: ':id',
@@ -5614,43 +5624,63 @@ class PropertyAgentViewingIndex extends BaseComponent {
5614
5624
  type: '',
5615
5625
  status: '',
5616
5626
  };
5617
- viewings = [];
5627
+ loading = false;
5628
+ viewings = signal([], ...(ngDevMode ? [{ debugName: "viewings" }] : []));
5629
+ meta;
5630
+ pageEvent;
5631
+ length = 100;
5632
+ pageSize = 15;
5633
+ pageSizeOptions = [5, 10, 25, 100];
5634
+ pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
5618
5635
  ngOnInit() {
5619
- this.route.queryParams.subscribe(({ status }) => {
5620
- this.select = this.links.findIndex((item) => item.status === status);
5636
+ const sub = this.route.queryParamMap
5637
+ .pipe(map((p) => {
5638
+ const page = p.get('page') ? Number(p.get('page')) : 1;
5639
+ this.pageIndex.set(Math.max(page - 1, 0));
5640
+ const status = p.get('status') || undefined;
5641
+ let filter = '';
5621
5642
  if (status) {
5622
- this.filterOptions.status = status.toUpperCase();
5623
- }
5624
- else {
5625
- delete this.filterOptions.status;
5643
+ this.select = this.links.findIndex((item) => item.status === status);
5644
+ filter = `status:${status?.toUpperCase()}`;
5626
5645
  }
5627
- this.findViewings();
5628
- });
5629
- }
5630
- findViewings() {
5631
- const options = {
5632
- sort: 'updatedAt desc',
5633
- };
5634
- const filterString = this.convertFilterOptions(this.filterOptions);
5635
- if (filterString) {
5636
- options['filter'] = filterString;
5637
- }
5638
- this.propertyService.findViewingsByAgent(options).subscribe({
5646
+ return {
5647
+ page,
5648
+ filter,
5649
+ limit: p.get('limit') ? Number(p.get('limit')) : 15,
5650
+ sort: p.get('sort') || undefined,
5651
+ };
5652
+ }),
5653
+ // Cheap deep compare via JSON to avoid spam calls when nothing changed
5654
+ map((o) => JSON.stringify(o)), distinctUntilChanged(), map((s) => JSON.parse(s)), switchMap((params) => {
5655
+ this.loading = true;
5656
+ params['sort'] = 'updatedAt desc';
5657
+ return this.propertyService.findViewingsByAgent(params).pipe(finalize(() => (this.loading = false)));
5658
+ }))
5659
+ .subscribe({
5639
5660
  next: (res) => {
5640
- this.viewings = res.data;
5661
+ this.viewings.set(res.data);
5662
+ this.meta = res.meta;
5663
+ this.length = res.meta.pagination.count;
5664
+ this.loading = false;
5665
+ },
5666
+ error: () => {
5667
+ this.viewings.set([]);
5668
+ this.length = 0;
5669
+ this.loading = false;
5641
5670
  },
5642
5671
  });
5672
+ // // auto-unsubscribe on destroy
5673
+ this.destroyRef.onDestroy(() => sub.unsubscribe());
5643
5674
  }
5644
- convertFilterOptions(jsonObj) {
5645
- return Object.entries(jsonObj)
5646
- .filter(([key, value]) => value !== '' && value !== undefined)
5647
- .map(([key, value]) => {
5648
- return `${key}:${value}`;
5649
- })
5650
- .join(',');
5675
+ onPage(e) {
5676
+ this.router.navigate([], {
5677
+ queryParams: { page: e.pageIndex + 1, limit: e.pageSize },
5678
+ queryParamsHandling: 'merge',
5679
+ replaceUrl: true, // optional: avoid stacking history on every page click
5680
+ });
5651
5681
  }
5652
5682
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyAgentViewingIndex, deps: null, target: i0.ɵɵFactoryTarget.Component });
5653
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyAgentViewingIndex, isStandalone: true, selector: "rolatech-property-agent-viewing-index", usesInheritance: true, ngImport: i0, template: "<rolatech-toolbar title=\"Viewings\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) { @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n } }\n</rolatech-tabs>\n<div>\n @if (viewings) { @for (item of viewings; track $index) {\n <rolatech-property-viewing-item [routerLink]=\"['./', item.id]\" [viewing]=\"item\"></rolatech-property-viewing-item>\n } } @else {\n <rolatech-empty></rolatech-empty>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: EmptyComponent, selector: "rolatech-empty" }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }] });
5683
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: PropertyAgentViewingIndex, isStandalone: true, selector: "rolatech-property-agent-viewing-index", usesInheritance: true, ngImport: i0, template: "<rolatech-toolbar title=\"Viewings\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) { @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n } }\n</rolatech-tabs>\n<div>\n @if (viewings(); as viewings) { @for (item of viewings; track $index) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <rolatech-empty></rolatech-empty>\n }\n</div>\n<mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n>\n</mat-paginator>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: EmptyComponent, selector: "rolatech-empty" }, { kind: "component", type: PropertyViewingItemComponent, selector: "rolatech-property-viewing-item", inputs: ["viewing"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i2$2.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }] });
5654
5684
  }
5655
5685
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PropertyAgentViewingIndex, decorators: [{
5656
5686
  type: Component,
@@ -5663,7 +5693,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
5663
5693
  RouterModule,
5664
5694
  EmptyComponent,
5665
5695
  PropertyViewingItemComponent,
5666
- ], template: "<rolatech-toolbar title=\"Viewings\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) { @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n } }\n</rolatech-tabs>\n<div>\n @if (viewings) { @for (item of viewings; track $index) {\n <rolatech-property-viewing-item [routerLink]=\"['./', item.id]\" [viewing]=\"item\"></rolatech-property-viewing-item>\n } } @else {\n <rolatech-empty></rolatech-empty>\n }\n</div>\n" }]
5696
+ MatPaginatorModule,
5697
+ ], template: "<rolatech-toolbar title=\"Viewings\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) { @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n } }\n</rolatech-tabs>\n<div>\n @if (viewings(); as viewings) { @for (item of viewings; track $index) {\n <rolatech-property-viewing-item\n [loading]=\"loading\"\n [routerLink]=\"['./', item.id]\"\n [viewing]=\"item\"\n ></rolatech-property-viewing-item>\n } } @else {\n <rolatech-empty></rolatech-empty>\n }\n</div>\n<mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n>\n</mat-paginator>\n" }]
5667
5698
  }] });
5668
5699
 
5669
5700
  class PropertyAgentViewingDetail extends BaseComponent {
@@ -6018,4 +6049,4 @@ const propertyManageOverviewRoutes = [
6018
6049
  */
6019
6050
 
6020
6051
  export { PropertyOfferTimelineStatus as A, PropertyOfferType as B, PropertyOfferStatus as C, ResidencyStatus as D, EmploymentStatus as E, PropertyApplicantType as F, AdverseCreditStatus as G, BedroomRange as H, PriceRange as I, Market as M, PropertyUtil as P, RentFrequency as R, ViewingTime as V, PropertyViewType as a, PropertyManageItemSkeleton as b, PropertyViewingItemComponent as c, propertyManageOffersRoutes as d, propertyRoutes as e, featureManageRoutes as f, propertyManageRoutes as g, propertyManageViewingsRoutes as h, propertyAgentViewingRoutes as i, propertyAgentOfferRoutes as j, propertyAgentOverviewRoutes as k, lettingManageRoutes as l, propertyManageOverviewRoutes as m, PropertyActionsComponent as n, PropertyItemComponent as o, propertyReviewRoutes as p, PropertyPricingComponent as q, PropertyPriceType as r, PropertyVideoProvider as s, tenantManageRoutes as t, PropertyStatus as u, PropertyType as v, PropertyInventoryStatus as w, PropertyScope as x, PropertyViewingStatus as y, PropertyViewerCategory as z };
6021
- //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-CXRHibz5.mjs.map
6052
+ //# sourceMappingURL=rolatech-angular-property-rolatech-angular-property-BwNQMVlJ.mjs.map