@smarterplan/ngx-smarterplan-locations 0.0.21

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 (50) hide show
  1. package/README.md +24 -0
  2. package/esm2020/lib/components/carousel/carousel.component.mjs +28 -0
  3. package/esm2020/lib/components/chevron/chevron.component.mjs +18 -0
  4. package/esm2020/lib/components/detail-location/detail-location.component.mjs +154 -0
  5. package/esm2020/lib/components/form-location/form-location.component.mjs +215 -0
  6. package/esm2020/lib/components/images/images.component.mjs +91 -0
  7. package/esm2020/lib/components/locations/locations.component.mjs +138 -0
  8. package/esm2020/lib/components/locations/map/map-popup/map-popup.component.mjs +66 -0
  9. package/esm2020/lib/components/locations/map/map.component.mjs +91 -0
  10. package/esm2020/lib/components/plans/calibration/calibration.component.mjs +484 -0
  11. package/esm2020/lib/components/plans/edit-plan/edit-plan.component.mjs +314 -0
  12. package/esm2020/lib/components/plans/plans.component.mjs +211 -0
  13. package/esm2020/lib/components/tab-navigation/tab-navigation.component.mjs +40 -0
  14. package/esm2020/lib/components/visits/visits.component.mjs +227 -0
  15. package/esm2020/lib/components/zones/add-zone/add-zone.component.mjs +592 -0
  16. package/esm2020/lib/components/zones/add-zone/selection/selection.component.mjs +77 -0
  17. package/esm2020/lib/components/zones/zones.component.mjs +214 -0
  18. package/esm2020/lib/helper.service.mjs +2 -0
  19. package/esm2020/lib/ngx-smarterplan-location-routing.module.mjs +49 -0
  20. package/esm2020/lib/ngx-smarterplan-locations.module.mjs +107 -0
  21. package/esm2020/lib/ngx-smarterplan-locations.service.mjs +14 -0
  22. package/esm2020/public-api.mjs +6 -0
  23. package/esm2020/smarterplan-ngx-smarterplan-locations.mjs +5 -0
  24. package/fesm2015/smarterplan-ngx-smarterplan-locations.mjs +3126 -0
  25. package/fesm2015/smarterplan-ngx-smarterplan-locations.mjs.map +1 -0
  26. package/fesm2020/smarterplan-ngx-smarterplan-locations.mjs +2987 -0
  27. package/fesm2020/smarterplan-ngx-smarterplan-locations.mjs.map +1 -0
  28. package/lib/components/carousel/carousel.component.d.ts +12 -0
  29. package/lib/components/chevron/chevron.component.d.ts +9 -0
  30. package/lib/components/detail-location/detail-location.component.d.ts +51 -0
  31. package/lib/components/form-location/form-location.component.d.ts +40 -0
  32. package/lib/components/images/images.component.d.ts +28 -0
  33. package/lib/components/locations/locations.component.d.ts +49 -0
  34. package/lib/components/locations/map/map-popup/map-popup.component.d.ts +22 -0
  35. package/lib/components/locations/map/map.component.d.ts +22 -0
  36. package/lib/components/plans/calibration/calibration.component.d.ts +140 -0
  37. package/lib/components/plans/edit-plan/edit-plan.component.d.ts +54 -0
  38. package/lib/components/plans/plans.component.d.ts +59 -0
  39. package/lib/components/tab-navigation/tab-navigation.component.d.ts +13 -0
  40. package/lib/components/visits/visits.component.d.ts +51 -0
  41. package/lib/components/zones/add-zone/add-zone.component.d.ts +154 -0
  42. package/lib/components/zones/add-zone/selection/selection.component.d.ts +44 -0
  43. package/lib/components/zones/zones.component.d.ts +66 -0
  44. package/lib/helper.service.d.ts +6 -0
  45. package/lib/ngx-smarterplan-location-routing.module.d.ts +7 -0
  46. package/lib/ngx-smarterplan-locations.module.d.ts +31 -0
  47. package/lib/ngx-smarterplan-locations.service.d.ts +6 -0
  48. package/package.json +39 -0
  49. package/public-api.d.ts +2 -0
  50. package/smarterplan-ngx-smarterplan-locations.d.ts +5 -0
@@ -0,0 +1,2987 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, EventEmitter, Component, Output, Input, ViewChild, HostListener, NgModule } from '@angular/core';
3
+ import * as i2 from 'ngx-smarterplan-core';
4
+ import { enumToArray, LevelStatus, uploadFileToS3, deleteFromS3, PropertyType, getSignedImageUrlForSpace, SearchObjectType, SpaceStatus, getMetaForImage, getSignedFile, downloadFileAsObject, downloadBlob, CaptureViewer, showScanPointsOnPlanInDiv, getCoefficientsForImage, wait, InventoryStatus, sortAlphabeticallyOnName, NgxSmarterplanCoreModule } from 'ngx-smarterplan-core';
5
+ import * as i1 from '@angular/router';
6
+ import { RouterModule } from '@angular/router';
7
+ import * as i5 from '@angular/common';
8
+ import { CommonModule } from '@angular/common';
9
+ import * as i3 from '@ngx-translate/core';
10
+ import { TranslateModule } from '@ngx-translate/core';
11
+ import { icon, Marker, tileLayer, latLng, marker, latLngBounds, point } from 'leaflet';
12
+ import * as i1$1 from '@asymmetrik/ngx-leaflet';
13
+ import { LeafletModule } from '@asymmetrik/ngx-leaflet';
14
+ import * as i1$2 from '@angular/forms';
15
+ import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
16
+ import * as i7 from '@ng-bootstrap/ng-bootstrap';
17
+ import { ModalDismissReasons, NgbModule } from '@ng-bootstrap/ng-bootstrap';
18
+ import * as i7$1 from 'ngx-clipboard';
19
+ import { ClipboardModule } from 'ngx-clipboard';
20
+ import { getDocument } from 'pdfjs-dist';
21
+ import panzoom from 'panzoom';
22
+ import { PdfViewerModule } from 'ng2-pdf-viewer';
23
+
24
+ class NgxSmarterplanLocationsService {
25
+ constructor() { }
26
+ }
27
+ NgxSmarterplanLocationsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxSmarterplanLocationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
28
+ NgxSmarterplanLocationsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxSmarterplanLocationsService, providedIn: 'root' });
29
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxSmarterplanLocationsService, decorators: [{
30
+ type: Injectable,
31
+ args: [{
32
+ providedIn: 'root'
33
+ }]
34
+ }], ctorParameters: function () { return []; } });
35
+
36
+ class TabNavigationComponent {
37
+ constructor(router) {
38
+ this.router = router;
39
+ this.onGoBack = new EventEmitter();
40
+ this.menuItems = [];
41
+ }
42
+ onClick(url, parameter, index) {
43
+ if (index === this.menuItems.length - 1) {
44
+ return;
45
+ }
46
+ if (url) {
47
+ const newURL = `/dashboard${url}`;
48
+ if (this.router.url !== newURL) {
49
+ this.router.navigate([newURL]);
50
+ }
51
+ else {
52
+ this.onGoBack.emit(parameter);
53
+ }
54
+ }
55
+ else {
56
+ this.onGoBack.emit(parameter);
57
+ }
58
+ }
59
+ }
60
+ TabNavigationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TabNavigationComponent, deps: [{ token: i1.Router }], target: i0.ɵɵFactoryTarget.Component });
61
+ TabNavigationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: { menuItems: "menuItems" }, outputs: { onGoBack: "onGoBack" }, ngImport: i0, template: "<span class=\"menu\">\n <span *ngFor=\"let item of menuItems; let index = index;\">\n <span class=\"item\" [ngClass]=\"{'current':index === menuItems.length - 1}\"\n (click)=\"onClick(item.url,item.back, index)\">{{item.label | translate}}</span> <span\n *ngIf=\"index !== menuItems.length - 1\" class=\"separator\">></span>\n </span>\n</span>\n", styles: [".item{font-size:1.35em;color:var(--smarterplan-primary);cursor:pointer}.current{color:var(--smarterplan-primary)!important;cursor:default!important;text-decoration:none!important}.separator{font-size:1.4em;margin-left:10px;margin-right:10px;color:var(--smarterplan-primary)}.item:hover{text-decoration:underline}.menu{margin-top:15px;margin-bottom:20px}\n"], directives: [{ type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], pipes: { "translate": i3.TranslatePipe } });
62
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: TabNavigationComponent, decorators: [{
63
+ type: Component,
64
+ args: [{ selector: 'lib-tab-navigation', template: "<span class=\"menu\">\n <span *ngFor=\"let item of menuItems; let index = index;\">\n <span class=\"item\" [ngClass]=\"{'current':index === menuItems.length - 1}\"\n (click)=\"onClick(item.url,item.back, index)\">{{item.label | translate}}</span> <span\n *ngIf=\"index !== menuItems.length - 1\" class=\"separator\">></span>\n </span>\n</span>\n", styles: [".item{font-size:1.35em;color:var(--smarterplan-primary);cursor:pointer}.current{color:var(--smarterplan-primary)!important;cursor:default!important;text-decoration:none!important}.separator{font-size:1.4em;margin-left:10px;margin-right:10px;color:var(--smarterplan-primary)}.item:hover{text-decoration:underline}.menu{margin-top:15px;margin-bottom:20px}\n"] }]
65
+ }], ctorParameters: function () { return [{ type: i1.Router }]; }, propDecorators: { onGoBack: [{
66
+ type: Output
67
+ }], menuItems: [{
68
+ type: Input
69
+ }] } });
70
+
71
+ class MapPopupComponent {
72
+ constructor(router, ngZone) {
73
+ this.router = router;
74
+ this.ngZone = ngZone;
75
+ this.isOnPopup = false;
76
+ this.isMuseumUser = true;
77
+ }
78
+ ngOnInit() {
79
+ this.marker.on("click", () => this.onClickVisitFirst());
80
+ this.marker.on("mouseover", () => {
81
+ this.isOnPopup = true;
82
+ if (!this.marker.isPopupOpen()) {
83
+ this.openPopup();
84
+ }
85
+ });
86
+ }
87
+ openPopup() {
88
+ this.marker.openPopup();
89
+ this.isOnPopup = false;
90
+ this.popuparea = document.querySelector(".leaflet-popup");
91
+ this.popuparea.addEventListener("mouseleave", () => {
92
+ this.closePopup();
93
+ });
94
+ this.popuparea.addEventListener("mouseover", () => {
95
+ this.isOnPopup = true;
96
+ });
97
+ this.marker.on("mouseout", () => {
98
+ this.closePopup();
99
+ });
100
+ }
101
+ closePopup() {
102
+ this.isOnPopup = false;
103
+ /** We want to wait to avoid glitch */
104
+ setTimeout(() => {
105
+ if (!this.isOnPopup) {
106
+ this.marker.closePopup();
107
+ }
108
+ }, 100);
109
+ }
110
+ onClickVisitFirst() {
111
+ if (this.space.visits.length === 0) {
112
+ console.log("No space to visit !");
113
+ }
114
+ else {
115
+ this.onVisitClick(this.space.visits[0]);
116
+ }
117
+ }
118
+ async onVisitClick(visit) {
119
+ this.ngZone.run(() => {
120
+ this.router.navigate(["visit", this.space.id], {
121
+ queryParams: { model3D: visit.model3d },
122
+ });
123
+ });
124
+ }
125
+ }
126
+ MapPopupComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MapPopupComponent, deps: [{ token: i1.Router }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
127
+ MapPopupComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: MapPopupComponent, selector: "lib-map-popup", ngImport: i0, template: "<div id=\"map-popup\">\n <div class=\"map-popup-header\" (click)=\"onClickVisitFirst()\" [style.cursor]=\"'pointer'\">\n <img [src]=\"space.annexes\" />\n </div>\n <div class=\"map-popup-details\">\n <h1 (click)=\"onClickVisitFirst()\" [style.cursor]=\"'pointer'\">{{space.name}}</h1>\n <span class=\"map-popup-address\">{{space.addresses}}</span>\n <span *ngFor=\"let visit of space.visits\" class=\"map-popup-visit\" (click)=\"onVisitClick(visit)\"\n [style.cursor]=\"'pointer'\">\u27A4 {{ visit.name ? visit.name : space.name}}</span>\n </div>\n</div>\n", styles: [".map-popup-header{height:120px;overflow:hidden;border-top-right-radius:20px;border-top-left-radius:20px}.map-popup-header img{width:100%;position:relative;top:50%;transform:translateY(-50%)}.map-popup-details{padding:5px 10px 10px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;background-color:#fff;color:#000}.map-popup-details{display:flex;flex-direction:column}.map-popup-details h1{font-size:1.5rem;font-weight:700;margin-bottom:0;color:var(--smarterplan-primary)}.map-popup-address{align-self:flex-end;margin-bottom:10px}.map-popup-visit{color:var(--smarterplan-primary);font-size:1.15rem}.map-popup-visit:hover{color:var(--smarterplan-primary)}\n"], directives: [{ type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
128
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MapPopupComponent, decorators: [{
129
+ type: Component,
130
+ args: [{ selector: 'lib-map-popup', template: "<div id=\"map-popup\">\n <div class=\"map-popup-header\" (click)=\"onClickVisitFirst()\" [style.cursor]=\"'pointer'\">\n <img [src]=\"space.annexes\" />\n </div>\n <div class=\"map-popup-details\">\n <h1 (click)=\"onClickVisitFirst()\" [style.cursor]=\"'pointer'\">{{space.name}}</h1>\n <span class=\"map-popup-address\">{{space.addresses}}</span>\n <span *ngFor=\"let visit of space.visits\" class=\"map-popup-visit\" (click)=\"onVisitClick(visit)\"\n [style.cursor]=\"'pointer'\">\u27A4 {{ visit.name ? visit.name : space.name}}</span>\n </div>\n</div>\n", styles: [".map-popup-header{height:120px;overflow:hidden;border-top-right-radius:20px;border-top-left-radius:20px}.map-popup-header img{width:100%;position:relative;top:50%;transform:translateY(-50%)}.map-popup-details{padding:5px 10px 10px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;background-color:#fff;color:#000}.map-popup-details{display:flex;flex-direction:column}.map-popup-details h1{font-size:1.5rem;font-weight:700;margin-bottom:0;color:var(--smarterplan-primary)}.map-popup-address{align-self:flex-end;margin-bottom:10px}.map-popup-visit{color:var(--smarterplan-primary);font-size:1.15rem}.map-popup-visit:hover{color:var(--smarterplan-primary)}\n"] }]
131
+ }], ctorParameters: function () { return [{ type: i1.Router }, { type: i0.NgZone }]; } });
132
+
133
+ const iconRetinaUrl = "assets/marker-icon-2x.png";
134
+ const iconUrl = "assets/marker-icon.png";
135
+ const shadowUrl = "assets/marker-shadow.png";
136
+ const iconDefault = icon({
137
+ iconRetinaUrl,
138
+ iconUrl,
139
+ shadowUrl,
140
+ iconSize: [25, 41],
141
+ iconAnchor: [12, 41],
142
+ popupAnchor: [1, -34],
143
+ tooltipAnchor: [16, -28],
144
+ shadowSize: [41, 41],
145
+ });
146
+ Marker.prototype.options.icon = iconDefault;
147
+ class MapComponent {
148
+ constructor(componentFactoryResolver, injector) {
149
+ this.componentFactoryResolver = componentFactoryResolver;
150
+ this.injector = injector;
151
+ this.options = {
152
+ layers: [
153
+ tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
154
+ maxZoom: 18,
155
+ attribution: "...",
156
+ }),
157
+ ],
158
+ zoom: 5.5,
159
+ center: latLng(47.06394, 2.77736),
160
+ };
161
+ this.layers = [];
162
+ }
163
+ ngOnChanges(changes) {
164
+ if (changes.spaces) {
165
+ const nm = [];
166
+ const bd = [];
167
+ if (!changes.spaces.currentValue) {
168
+ return;
169
+ }
170
+ // Filtering out spaces without lat/longitudes
171
+ for (const s of changes.spaces.currentValue) {
172
+ if (s.latitude === null || s.longitude === null) {
173
+ console.log(`${s.name} has not coordinates for ${s.addresses}`);
174
+ continue;
175
+ }
176
+ // We use the popup system from leaflet, as it packs a few improvements
177
+ // on using bootstrap: autoclose other popups, closing option.
178
+ const m = marker([s.latitude, s.longitude]);
179
+ m.bindPopup(this.createPopupComponent(s, m));
180
+ nm.push(m);
181
+ bd.push(latLng(s.latitude, s.longitude));
182
+ }
183
+ // Note how we set the layers in one assignment, in order to have leaflet
184
+ // detects the change correctly
185
+ // We also set the bounds afterward, for the map to be properly zoomed and
186
+ // centered
187
+ this.layers = nm;
188
+ if (this.map && bd.length > 0) {
189
+ this.map.fitBounds(latLngBounds(bd), {
190
+ padding: point(48, 48),
191
+ maxZoom: 12,
192
+ animate: true,
193
+ });
194
+ }
195
+ }
196
+ }
197
+ onMapReady(map) {
198
+ this.map = map;
199
+ }
200
+ createPopupComponent(space, marker) {
201
+ const comp = this.componentFactoryResolver
202
+ .resolveComponentFactory(MapPopupComponent)
203
+ .create(this.injector);
204
+ comp.instance.space = space;
205
+ comp.instance.marker = marker;
206
+ comp.changeDetectorRef.detectChanges();
207
+ return comp.location.nativeElement;
208
+ }
209
+ }
210
+ MapComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MapComponent, deps: [{ token: i0.ComponentFactoryResolver }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
211
+ MapComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: MapComponent, selector: "lib-map", inputs: { spaces: "spaces" }, usesOnChanges: true, ngImport: i0, template: "<div [leafletLayers]=\"layers\" [leafletOptions]=\"options\" (leafletMapReady)=\"onMapReady($event)\" id=\"map\" leaflet\n style=\"height: 500px; margin-top: 30px; z-index: 195\"></div>\n", styles: [""], directives: [{ type: i1$1.LeafletDirective, selector: "[leaflet]", inputs: ["leafletFitBoundsOptions", "leafletPanOptions", "leafletZoomOptions", "leafletZoomPanOptions", "leafletOptions", "leafletZoom", "leafletCenter", "leafletFitBounds", "leafletMaxBounds", "leafletMinZoom", "leafletMaxZoom"], outputs: ["leafletMapReady", "leafletZoomChange", "leafletCenterChange", "leafletClick", "leafletDoubleClick", "leafletMouseDown", "leafletMouseUp", "leafletMouseMove", "leafletMouseOver", "leafletMouseOut", "leafletMapMove", "leafletMapMoveStart", "leafletMapMoveEnd", "leafletMapZoom", "leafletMapZoomStart", "leafletMapZoomEnd"] }, { type: i1$1.LeafletLayersDirective, selector: "[leafletLayers]", inputs: ["leafletLayers"] }] });
212
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: MapComponent, decorators: [{
213
+ type: Component,
214
+ args: [{ selector: 'lib-map', template: "<div [leafletLayers]=\"layers\" [leafletOptions]=\"options\" (leafletMapReady)=\"onMapReady($event)\" id=\"map\" leaflet\n style=\"height: 500px; margin-top: 30px; z-index: 195\"></div>\n", styles: [""] }]
215
+ }], ctorParameters: function () { return [{ type: i0.ComponentFactoryResolver }, { type: i0.Injector }]; }, propDecorators: { spaces: [{
216
+ type: Input
217
+ }] } });
218
+
219
+ class FormLocationComponent {
220
+ constructor(spaceService, propertyService, userService, fb, missionService, layerService) {
221
+ this.spaceService = spaceService;
222
+ this.propertyService = propertyService;
223
+ this.userService = userService;
224
+ this.fb = fb;
225
+ this.missionService = missionService;
226
+ this.layerService = layerService;
227
+ this.isSubmitted = false;
228
+ this.addMissionForNewSpace = true;
229
+ this.imageToBeDeleted = false;
230
+ this.onChange = new EventEmitter();
231
+ this.levels = enumToArray(LevelStatus);
232
+ }
233
+ ngOnInit() {
234
+ if (this.location) {
235
+ this.initEditForm();
236
+ }
237
+ else {
238
+ this.initForm();
239
+ }
240
+ this.handleInputImageText();
241
+ }
242
+ initForm() {
243
+ this.form = this.fb.group({
244
+ name: [null, [Validators.required]],
245
+ addresses: [""],
246
+ latitude: [null],
247
+ longitude: [null],
248
+ level: LevelStatus.LOW,
249
+ classGes: [null],
250
+ classEnergyConsumption: [null],
251
+ constructionYear: [null],
252
+ });
253
+ }
254
+ initEditForm() {
255
+ this.form = this.fb.group({
256
+ id: this.location.id,
257
+ name: [this.location.name, [Validators.required]],
258
+ addresses: this.location.addresses,
259
+ latitude: this.location.latitude,
260
+ longitude: this.location.longitude,
261
+ level: this.location.level,
262
+ classGes: this.location.classGes,
263
+ classEnergyConsumption: this.location.classEnergyConsumption,
264
+ constructionYear: this.location.constructionYear,
265
+ });
266
+ }
267
+ handleImageInput(target) {
268
+ this.imageToUpload = target.files[0];
269
+ if (this.imageToUpload) {
270
+ this.imageInputContainer.nativeElement.style.display = "none";
271
+ const reader = new FileReader();
272
+ reader.addEventListener("load", () => {
273
+ this.currentImage = reader.result;
274
+ });
275
+ reader.readAsDataURL(this.imageToUpload);
276
+ }
277
+ }
278
+ async uploadImage(spaceID) {
279
+ if (this.imageToUpload) {
280
+ uploadFileToS3("locations/", this.imageToUpload, spaceID).then(async (url) => {
281
+ if (url) {
282
+ await this.spaceService.updateSpace({
283
+ id: spaceID,
284
+ annexes: url,
285
+ });
286
+ }
287
+ });
288
+ }
289
+ else if (this.imageToBeDeleted) {
290
+ await this.spaceService.updateSpace({
291
+ id: spaceID,
292
+ annexes: null,
293
+ });
294
+ await deleteFromS3(this.location.annexes);
295
+ }
296
+ }
297
+ clearImageFile() {
298
+ this.imageToUpload = null;
299
+ this.currentImage = null;
300
+ this.imageInputContainer.nativeElement.style.display = "flex";
301
+ this.imageInput.nativeElement.value = "";
302
+ this.imageInputText = "Add picture";
303
+ }
304
+ async getLayerByName(name) {
305
+ const currentOrgId = this.userService.currentOrganisation().id;
306
+ const layers = await this.layerService.getLayerByNameForOrganisation(name, currentOrgId);
307
+ if (layers) {
308
+ return layers[0];
309
+ }
310
+ // create then
311
+ return this.layerService.createLayerForOrganisation(name, currentOrgId);
312
+ }
313
+ async onSubmit() {
314
+ this.isSubmitted = true;
315
+ if (!this.form.valid) {
316
+ return;
317
+ }
318
+ try {
319
+ let space;
320
+ if (this.location) {
321
+ this.changeCommaToDot();
322
+ space = await this.spaceService.updateSpace(this.form.value);
323
+ if (space) {
324
+ await this.uploadImage(space.id);
325
+ this.location = space;
326
+ this.onChange.emit(space);
327
+ }
328
+ }
329
+ else {
330
+ this.changeCommaToDot();
331
+ let layer = await this.getLayerByName("BUILDING");
332
+ if (!layer) {
333
+ layer = await this.layerService.createLayerForOrganisation("BUILDING", this.userService.currentOrganisation().id);
334
+ }
335
+ space = await this.spaceService.createSpace(this.form.value, layer);
336
+ const missions = this.userService.getManagerMissions();
337
+ if (space && missions.length > 0) {
338
+ const exampleMission = missions[0];
339
+ await this.uploadImage(space.id);
340
+ await this.propertyService.createProperty({
341
+ type: PropertyType.PROPERTY,
342
+ spaceID: space.id,
343
+ organisationID: exampleMission.organisationID,
344
+ });
345
+ // add directly a mission for this space
346
+ if (this.addMissionForNewSpace) {
347
+ await this.missionService.create({
348
+ spaceID: space.id,
349
+ userID: exampleMission.userID,
350
+ role: exampleMission.role,
351
+ level: exampleMission.level,
352
+ organisationID: exampleMission.organisationID,
353
+ orderedOrganisationID: exampleMission.organisationID,
354
+ });
355
+ }
356
+ this.onChange.emit(space);
357
+ }
358
+ else {
359
+ this.onChange.emit(null);
360
+ }
361
+ }
362
+ }
363
+ catch (error) {
364
+ console.error(error);
365
+ this.onChange.emit(null);
366
+ }
367
+ }
368
+ onCancel() {
369
+ this.form.reset();
370
+ this.onChange.emit(null);
371
+ }
372
+ get name() {
373
+ return this.form.get("name");
374
+ }
375
+ changeCommaToDot() {
376
+ const updatedSpace = this.form.value;
377
+ if (updatedSpace.latitude &&
378
+ updatedSpace.latitude.toString().includes(",")) {
379
+ updatedSpace.latitude = updatedSpace.latitude
380
+ .toString()
381
+ .replace(",", ".");
382
+ Number.parseFloat(updatedSpace.latitude);
383
+ }
384
+ if (updatedSpace.longitude &&
385
+ updatedSpace.longitude.toString().includes(",")) {
386
+ updatedSpace.longitude = updatedSpace.longitude
387
+ .toString()
388
+ .replace(",", ".");
389
+ Number.parseFloat(updatedSpace.longitude);
390
+ }
391
+ }
392
+ handleInputImageText() {
393
+ if (this.location && this.location.annexes) {
394
+ this.imageInputText = "Change picture";
395
+ getSignedImageUrlForSpace(this.location).then((url) => {
396
+ if (url) {
397
+ this.currentImage = url;
398
+ }
399
+ });
400
+ }
401
+ else {
402
+ this.imageInputText = "Add picture";
403
+ }
404
+ }
405
+ }
406
+ FormLocationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FormLocationComponent, deps: [{ token: i2.SpaceService }, { token: i2.PropertyService }, { token: i2.BaseUserService }, { token: i1$2.FormBuilder }, { token: i2.MissionService }, { token: i2.LayerService }], target: i0.ɵɵFactoryTarget.Component });
407
+ FormLocationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: FormLocationComponent, selector: "lib-form-location", inputs: { location: "location" }, outputs: { onChange: "onChange" }, viewQueries: [{ propertyName: "imageInputContainer", first: true, predicate: ["imageInputContainer"], descendants: true }, { propertyName: "imageInput", first: true, predicate: ["imageInput"], descendants: true }, { propertyName: "imageBasePreview", first: true, predicate: ["imageBasePreview"], descendants: true }], ngImport: i0, template: "<div class=\"col-md-6 form-new-loc\" *ngIf=\"form\">\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"form\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Name' | translate}} *</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" [class.is-invalid]=\"name.invalid && isSubmitted\" required\n formControlName=\"name\">\n <div class=\"invalid-feedback\">\n {{'Name is mandatory' | translate}}\n </div>\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Address' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"addresses\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Construction year' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"constructionYear\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Latitude' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"latitude\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Longitude' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"longitude\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Access level' | translate}}</label>\n <div class=\"col-sm-9\">\n <select class=\"d-inline-block custom-select rounded-pill\" formControlName=\"level\">\n <option *ngFor=\"let level of levels\" [value]=\"level\">{{level | translate}}</option>\n </select>\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Energy Class' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"classEnergyConsumption\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Greenhouse gas emission Class' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"classGes\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Cover picture' | translate}}</label>\n <div class=\"col-sm-9\">\n <img #imageBasePreview *ngIf=\"currentImage\" class=\"image-preview\" [src]=\"currentImage\">\n <label #imageInputContainer for=\"image\"\n class=\"btn btn-label-file btn-outline-primary rounded-pill imageInputContainer\">\n <input #imageInput type=\"file\" id=\"image\" (change)=\"handleImageInput($event.target)\" />\n {{ imageInputText | translate}}</label>\n <button *ngIf=\"currentImage && !imageToUpload\" \n class=\"btn btn-outline-primary rounded-pill\" (click)=\"imageToBeDeleted=true;clearImageFile()\">{{'Delete picture' | translate }}</button>\n <div class=\"image-name\" *ngIf=\"imageToUpload\">\n <p >{{ imageToUpload.name}}</p>\n <div (click)=\"clearImageFile()\" [style.cursor]=\"'pointer'\">\n <span class=\"iconify\" data-icon=\"mdi:trash-can-outline\"></span>\n </div>\n </div>\n \n </div>\n </div>\n <div class=\"mb-3 row\" *ngIf=\"!location\">\n <label class=\"col-sm-3 col-form-label\">{{'Create mission for this space' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"addMissionForNewSpace\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n \n <button type=\"submit\" class=\"btn btn-outline-primary rounded-pill me-3\">{{'Save' | translate}}</button>\n <button type=\"button\" (click)=\"onCancel()\" class=\"btn btn-outline-primary rounded-pill\">{{'Cancel' |\n translate}}</button>\n </form>\n</div>\n", styles: ["input[type=file]{display:none}.custom-select{border-color:var(--smarterplan-primary);width:auto}.form-new-loc{background-color:#d3d3d3;padding-top:10px;padding-bottom:10px;margin-bottom:15px}.btn-outline-primary{background-color:#fff}h4{color:var(--smarterplan-primary)}.image-preview{object-fit:cover;max-height:400px;max-width:400px;display:flex;margin-bottom:15px}.imageInputContainer{width:-moz-fit-content;width:fit-content;height:-moz-fit-content;height:fit-content}.image-name{display:flex}.col-form-label{margin-bottom:1rem;width:230px}.form-new-loc{background-color:#d3d3d3;margin-bottom:15px;padding:10px;width:80%;border-radius:10px}.col-sm-9 select{width:100%;height:31.25px;padding-left:10px}.btn-outline-primary{width:200px;margin-bottom:1rem;margin-right:1rem}\n"], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "translate": i3.TranslatePipe } });
408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: FormLocationComponent, decorators: [{
409
+ type: Component,
410
+ args: [{ selector: 'lib-form-location', template: "<div class=\"col-md-6 form-new-loc\" *ngIf=\"form\">\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"form\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Name' | translate}} *</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" [class.is-invalid]=\"name.invalid && isSubmitted\" required\n formControlName=\"name\">\n <div class=\"invalid-feedback\">\n {{'Name is mandatory' | translate}}\n </div>\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Address' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"addresses\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Construction year' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"constructionYear\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Latitude' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"latitude\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Longitude' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"longitude\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Access level' | translate}}</label>\n <div class=\"col-sm-9\">\n <select class=\"d-inline-block custom-select rounded-pill\" formControlName=\"level\">\n <option *ngFor=\"let level of levels\" [value]=\"level\">{{level | translate}}</option>\n </select>\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Energy Class' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"classEnergyConsumption\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Greenhouse gas emission Class' | translate}}</label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"classGes\">\n </div>\n </div>\n \n <div class=\"mb-3 row\">\n <label class=\"col-sm-3 col-form-label\">{{'Cover picture' | translate}}</label>\n <div class=\"col-sm-9\">\n <img #imageBasePreview *ngIf=\"currentImage\" class=\"image-preview\" [src]=\"currentImage\">\n <label #imageInputContainer for=\"image\"\n class=\"btn btn-label-file btn-outline-primary rounded-pill imageInputContainer\">\n <input #imageInput type=\"file\" id=\"image\" (change)=\"handleImageInput($event.target)\" />\n {{ imageInputText | translate}}</label>\n <button *ngIf=\"currentImage && !imageToUpload\" \n class=\"btn btn-outline-primary rounded-pill\" (click)=\"imageToBeDeleted=true;clearImageFile()\">{{'Delete picture' | translate }}</button>\n <div class=\"image-name\" *ngIf=\"imageToUpload\">\n <p >{{ imageToUpload.name}}</p>\n <div (click)=\"clearImageFile()\" [style.cursor]=\"'pointer'\">\n <span class=\"iconify\" data-icon=\"mdi:trash-can-outline\"></span>\n </div>\n </div>\n \n </div>\n </div>\n <div class=\"mb-3 row\" *ngIf=\"!location\">\n <label class=\"col-sm-3 col-form-label\">{{'Create mission for this space' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"addMissionForNewSpace\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n \n <button type=\"submit\" class=\"btn btn-outline-primary rounded-pill me-3\">{{'Save' | translate}}</button>\n <button type=\"button\" (click)=\"onCancel()\" class=\"btn btn-outline-primary rounded-pill\">{{'Cancel' |\n translate}}</button>\n </form>\n</div>\n", styles: ["input[type=file]{display:none}.custom-select{border-color:var(--smarterplan-primary);width:auto}.form-new-loc{background-color:#d3d3d3;padding-top:10px;padding-bottom:10px;margin-bottom:15px}.btn-outline-primary{background-color:#fff}h4{color:var(--smarterplan-primary)}.image-preview{object-fit:cover;max-height:400px;max-width:400px;display:flex;margin-bottom:15px}.imageInputContainer{width:-moz-fit-content;width:fit-content;height:-moz-fit-content;height:fit-content}.image-name{display:flex}.col-form-label{margin-bottom:1rem;width:230px}.form-new-loc{background-color:#d3d3d3;margin-bottom:15px;padding:10px;width:80%;border-radius:10px}.col-sm-9 select{width:100%;height:31.25px;padding-left:10px}.btn-outline-primary{width:200px;margin-bottom:1rem;margin-right:1rem}\n"] }]
411
+ }], ctorParameters: function () { return [{ type: i2.SpaceService }, { type: i2.PropertyService }, { type: i2.BaseUserService }, { type: i1$2.FormBuilder }, { type: i2.MissionService }, { type: i2.LayerService }]; }, propDecorators: { imageInputContainer: [{
412
+ type: ViewChild,
413
+ args: ["imageInputContainer"]
414
+ }], imageInput: [{
415
+ type: ViewChild,
416
+ args: ["imageInput"]
417
+ }], imageBasePreview: [{
418
+ type: ViewChild,
419
+ args: ["imageBasePreview"]
420
+ }], location: [{
421
+ type: Input
422
+ }], onChange: [{
423
+ type: Output
424
+ }] } });
425
+
426
+ class LocationsComponent {
427
+ constructor(spaceService, router, userService, viewerService, navigationService, navigatorService) {
428
+ this.spaceService = spaceService;
429
+ this.router = router;
430
+ this.userService = userService;
431
+ this.viewerService = viewerService;
432
+ this.navigationService = navigationService;
433
+ this.navigatorService = navigatorService;
434
+ this.isAdding = false;
435
+ this.filteredLocations = [];
436
+ this.isManager = false;
437
+ this.isMuseumUser = true;
438
+ this.locations = [];
439
+ this.menuItems = [];
440
+ /** Last filtered Locations, used when we go back on all locations from the search bar location */
441
+ this.lastFiltredLocations = [];
442
+ this.levels = enumToArray(LevelStatus);
443
+ this.userSubcription = this.userService.isChanged.subscribe((isConnected) => {
444
+ if (isConnected) {
445
+ this.refresh();
446
+ }
447
+ });
448
+ this.locationIDSubscription =
449
+ this.navigatorService.locationIDChange.subscribe((id) => {
450
+ this.changeSelectedLocation(id);
451
+ this.setupMenuItems();
452
+ });
453
+ }
454
+ async ngOnInit() {
455
+ if (this.userService.cu) {
456
+ this.resetList();
457
+ this.updateUserRights();
458
+ }
459
+ this.setupMenuItems();
460
+ }
461
+ findSpaceById(id) {
462
+ return this.locations.find((loc) => loc.id === id);
463
+ }
464
+ /**
465
+ * Update when we change the selected location in filter (search bar)
466
+ * @param id for a location, if null display all last filtred location
467
+ */
468
+ changeSelectedLocation(id) {
469
+ const space = id ? this.findSpaceById(id) : null;
470
+ this.filteredLocations = space ? [space] : this.lastFiltredLocations; // New selected space or last filtered location
471
+ }
472
+ refresh() {
473
+ this.updateUserRights();
474
+ this.resetList();
475
+ }
476
+ ngOnDestroy() {
477
+ this.userSubcription.unsubscribe();
478
+ this.locationIDSubscription.unsubscribe();
479
+ }
480
+ setupMenuItems() {
481
+ this.menuItems = [{ label: "All Locations", url: "/localisation" }];
482
+ if (this.isAdding) {
483
+ this.menuItems.push({ label: "New" });
484
+ }
485
+ if (this.filteredLocations.length === 1) {
486
+ const selectedLoc = this.filteredLocations[0];
487
+ this.menuItems.push({ label: selectedLoc.name });
488
+ }
489
+ }
490
+ updateUserRights() {
491
+ this.isManager = this.userService.hasManagerRoleInAnyMission();
492
+ this.isMuseumUser = this.userService.hasOnlyMuseumRole();
493
+ }
494
+ isManagerForSpace(spaceID) {
495
+ return (this.userService.isManager(spaceID) ||
496
+ this.userService.isGuide(spaceID));
497
+ }
498
+ async resetList() {
499
+ this.locations = await this.spaceService.getLocationsForCurrentUser();
500
+ this.filteredLocations = this.locations;
501
+ this.lastFiltredLocations = this.locations;
502
+ this.isAdding = false;
503
+ this.changeSelectedLocation(this.navigatorService.currentNavBarLocationID); // Set for the current selected location
504
+ this.setupMenuItems();
505
+ }
506
+ onAddLocation() {
507
+ this.isAdding = true;
508
+ this.setupMenuItems();
509
+ }
510
+ handleImageInput(files) {
511
+ this.imageToUpload = files.item(0);
512
+ }
513
+ onFormChange(event) {
514
+ this.navigatorService.locationIDChange.next(null); /** Remove selected location */
515
+ this.isAdding = false;
516
+ if (event) {
517
+ this.resetList();
518
+ }
519
+ this.setupMenuItems();
520
+ }
521
+ async onVisitClick(loc, visit) {
522
+ if (!this.userService.userHasAccessToAllZonesInSpace(loc.id)) {
523
+ // get allowed sweep for the user
524
+ const allowedSweeps = await this.navigationService.getAllowedSweepsIDsForUserInSpace(loc.id);
525
+ const firstSweep = allowedSweeps[0];
526
+ this.viewerService.setSweepToMove(firstSweep);
527
+ }
528
+ if (this.userService.isGuide(loc.id)) {
529
+ this.router.navigate(["museum", loc.id]);
530
+ }
531
+ else {
532
+ this.router.navigate(["visit", loc.id], {
533
+ queryParams: { model3D: visit.model3d },
534
+ });
535
+ }
536
+ }
537
+ applyResultsSearchBar(results) {
538
+ this.filteredLocations = results || this.locations;
539
+ this.lastFiltredLocations = this.filteredLocations;
540
+ this.navigatorService.locationIDChange.next(null); // When we search with keyword, we would like to search on all locations
541
+ }
542
+ getSearchType() {
543
+ return SearchObjectType.LOCATION;
544
+ }
545
+ }
546
+ LocationsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LocationsComponent, deps: [{ token: i2.SpaceService }, { token: i1.Router }, { token: i2.BaseUserService }, { token: i2.ViewerService }, { token: i2.NavigationService }, { token: i2.NavigatorService }], target: i0.ɵɵFactoryTarget.Component });
547
+ LocationsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: LocationsComponent, selector: "lib-locations", ngImport: i0, template: "<div class=\"dashboard-tab\">\n <div class=\"header-dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation (onGoBack)=\"onFormChange(null)\" [menuItems]='menuItems'></lib-tab-navigation>\n <span class=\"ml-3 refreshIcon\" (click)=\"refresh()\" *ngIf=\"!(isAdding)\" ngbTooltip=\"Refresh list\">\n <span class=\"iconify refreshIcon\" data-icon=\"ion:refresh-circle\" data-width=\"35\" data-rotate=\"90deg\"></span>\n </span>\n </div> \n <lib-search-bar *ngIf=\"!isAdding && !isMuseumUser\" [searchable]=\"locations\" [objectType]=\"getSearchType()\"\n (searchEvent)=\"applyResultsSearchBar($event)\"></lib-search-bar>\n \n <div class=\"col-md-8\">\n <div class=\"sp-row\" *ngIf=\"!isAdding && isManager\">\n <button class=\"btn btn-outline-primary rounded-pill add-location\" (click)=\"onAddLocation()\">\n {{'Add location' | translate}}</button>\n <!-- <div class=\"outer_circle\">\n <button id='btn-icon' (click)=\"onAddLocation()\"></button>\n </div> -->\n </div>\n </div>\n </div>\n <hr>\n <div class=\"row\" *ngIf=\"isAdding\">\n <div class=\"col-md-6\">\n <h4>{{'New location' | translate}}</h4>\n </div>\n </div>\n <section *ngIf=\"!isAdding\">\n <div class=\"row\">\n <div class=\"col-sm-12 col-md-12 col-lg-6\" *ngIf=\"filteredLocations\">\n <ul class=\"list-group list-group-flush\">\n <li class=\"d-flex justify-content-between list-group-item\"\n *ngFor=\"let loc of filteredLocations; index as index\">\n <div class=\"d-flex\">\n <div class=\"col-md-5 img-main-container\">\n <div id=\"img_container\">\n <img class=\"location-img\" *ngIf=\"loc.annexes; else elseBlock\" [src]=\"loc.annexes\">\n <ng-template #elseBlock><img class=\"location-img\"\n src=\"./../../../../assets/images/visuel-off-big.jpg\" />\n </ng-template>\n <button *ngIf=\"isManagerForSpace(loc.id)\" class=\"btn-details\" [routerLink]=\"[loc.id]\">\n {{'View details' | translate}}</button>\n </div>\n \n </div>\n <div class=\"col-md-7 location-info\">\n <p>{{loc.name}}</p>\n <p>{{loc.addresses}}</p>\n <p *ngFor=\"let visit of loc.visits\" (click)=\"onVisitClick(loc, visit)\" class=\"visit-name\"\n [style.cursor]=\"'pointer'\" ngbTooltip=\"Visit\">{{ visit.name ? visit.name : loc.name}}</p>\n </div>\n </div>\n </li>\n \n </ul>\n </div>\n <div class=\"col-sm-12 col-md-12 col-lg-6\">\n <lib-map [spaces]=\"filteredLocations\"></lib-map>\n </div>\n </div>\n </section>\n <lib-form-location *ngIf=\"isAdding\" (onChange)=\"onFormChange($event)\"></lib-form-location>\n</div>\n", styles: [".searchbar{margin-bottom:10px}input[type=file]{display:none}.form-control{border-radius:var(--form-control-border-radius)}.custom-select{border-color:var(--smarterplan-primary);width:auto}.form-new-loc{background-color:#d3d3d3;padding-top:10px;padding-bottom:10px;margin-bottom:15px}.btn-outline-primary{background-color:#fff}h4{color:var(--smarterplan-primary)}section .list-group{margin-top:15px}section .list-group-item{padding:5px 0}section .location-info{font-size:1.1em}section .location-img{width:100%;object-fit:cover;object-position:center bottom;height:165px}#img_container{position:relative;display:inline-block;text-align:center;width:250px}.img-main-container{max-width:250px;width:250px!important;margin-right:10px}.visit-name{color:var(--smarterplan-primary);font-weight:600}.btn-details{background-color:var(--smarterplan-primary-transparent);position:absolute;color:#fff;bottom:0px;right:0px;width:100%;height:30%;border:none;font-size:1em;text-transform:lowercase}#btn-icon{width:30px;height:30px;background:url(../../../../assets/icons/plus.svg);border:none;vertical-align:middle}#btn-icon:hover{background:url(../../../../assets/icons/plus-over.svg)}#btn-icon[disabled]:hover{background:url(../../../../assets/icons/plus.svg)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px;margin-left:5px}.refreshIcon{color:var(--smarterplan-primary);cursor:pointer}.refreshIcon svg{vertical-align:middle!important}.add-location{width:12vw}.sp-row{display:flex;flex-direction:row}\n"], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }, { type: i2.SearchBarComponent, selector: "lib-search-bar", inputs: ["searchable", "objectType"], outputs: ["searchEvent"] }, { type: MapComponent, selector: "lib-map", inputs: ["spaces"] }, { type: FormLocationComponent, selector: "lib-form-location", inputs: ["location"], outputs: ["onChange"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "triggers", "container", "disableTooltip", "tooltipClass", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.RouterLink, selector: ":not(a):not(area)[routerLink]", inputs: ["queryParams", "fragment", "queryParamsHandling", "preserveFragment", "skipLocationChange", "replaceUrl", "state", "relativeTo", "routerLink"] }], pipes: { "translate": i3.TranslatePipe } });
548
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LocationsComponent, decorators: [{
549
+ type: Component,
550
+ args: [{ selector: 'lib-locations', template: "<div class=\"dashboard-tab\">\n <div class=\"header-dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation (onGoBack)=\"onFormChange(null)\" [menuItems]='menuItems'></lib-tab-navigation>\n <span class=\"ml-3 refreshIcon\" (click)=\"refresh()\" *ngIf=\"!(isAdding)\" ngbTooltip=\"Refresh list\">\n <span class=\"iconify refreshIcon\" data-icon=\"ion:refresh-circle\" data-width=\"35\" data-rotate=\"90deg\"></span>\n </span>\n </div> \n <lib-search-bar *ngIf=\"!isAdding && !isMuseumUser\" [searchable]=\"locations\" [objectType]=\"getSearchType()\"\n (searchEvent)=\"applyResultsSearchBar($event)\"></lib-search-bar>\n \n <div class=\"col-md-8\">\n <div class=\"sp-row\" *ngIf=\"!isAdding && isManager\">\n <button class=\"btn btn-outline-primary rounded-pill add-location\" (click)=\"onAddLocation()\">\n {{'Add location' | translate}}</button>\n <!-- <div class=\"outer_circle\">\n <button id='btn-icon' (click)=\"onAddLocation()\"></button>\n </div> -->\n </div>\n </div>\n </div>\n <hr>\n <div class=\"row\" *ngIf=\"isAdding\">\n <div class=\"col-md-6\">\n <h4>{{'New location' | translate}}</h4>\n </div>\n </div>\n <section *ngIf=\"!isAdding\">\n <div class=\"row\">\n <div class=\"col-sm-12 col-md-12 col-lg-6\" *ngIf=\"filteredLocations\">\n <ul class=\"list-group list-group-flush\">\n <li class=\"d-flex justify-content-between list-group-item\"\n *ngFor=\"let loc of filteredLocations; index as index\">\n <div class=\"d-flex\">\n <div class=\"col-md-5 img-main-container\">\n <div id=\"img_container\">\n <img class=\"location-img\" *ngIf=\"loc.annexes; else elseBlock\" [src]=\"loc.annexes\">\n <ng-template #elseBlock><img class=\"location-img\"\n src=\"./../../../../assets/images/visuel-off-big.jpg\" />\n </ng-template>\n <button *ngIf=\"isManagerForSpace(loc.id)\" class=\"btn-details\" [routerLink]=\"[loc.id]\">\n {{'View details' | translate}}</button>\n </div>\n \n </div>\n <div class=\"col-md-7 location-info\">\n <p>{{loc.name}}</p>\n <p>{{loc.addresses}}</p>\n <p *ngFor=\"let visit of loc.visits\" (click)=\"onVisitClick(loc, visit)\" class=\"visit-name\"\n [style.cursor]=\"'pointer'\" ngbTooltip=\"Visit\">{{ visit.name ? visit.name : loc.name}}</p>\n </div>\n </div>\n </li>\n \n </ul>\n </div>\n <div class=\"col-sm-12 col-md-12 col-lg-6\">\n <lib-map [spaces]=\"filteredLocations\"></lib-map>\n </div>\n </div>\n </section>\n <lib-form-location *ngIf=\"isAdding\" (onChange)=\"onFormChange($event)\"></lib-form-location>\n</div>\n", styles: [".searchbar{margin-bottom:10px}input[type=file]{display:none}.form-control{border-radius:var(--form-control-border-radius)}.custom-select{border-color:var(--smarterplan-primary);width:auto}.form-new-loc{background-color:#d3d3d3;padding-top:10px;padding-bottom:10px;margin-bottom:15px}.btn-outline-primary{background-color:#fff}h4{color:var(--smarterplan-primary)}section .list-group{margin-top:15px}section .list-group-item{padding:5px 0}section .location-info{font-size:1.1em}section .location-img{width:100%;object-fit:cover;object-position:center bottom;height:165px}#img_container{position:relative;display:inline-block;text-align:center;width:250px}.img-main-container{max-width:250px;width:250px!important;margin-right:10px}.visit-name{color:var(--smarterplan-primary);font-weight:600}.btn-details{background-color:var(--smarterplan-primary-transparent);position:absolute;color:#fff;bottom:0px;right:0px;width:100%;height:30%;border:none;font-size:1em;text-transform:lowercase}#btn-icon{width:30px;height:30px;background:url(../../../../assets/icons/plus.svg);border:none;vertical-align:middle}#btn-icon:hover{background:url(../../../../assets/icons/plus-over.svg)}#btn-icon[disabled]:hover{background:url(../../../../assets/icons/plus.svg)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px;margin-left:5px}.refreshIcon{color:var(--smarterplan-primary);cursor:pointer}.refreshIcon svg{vertical-align:middle!important}.add-location{width:12vw}.sp-row{display:flex;flex-direction:row}\n"] }]
551
+ }], ctorParameters: function () { return [{ type: i2.SpaceService }, { type: i1.Router }, { type: i2.BaseUserService }, { type: i2.ViewerService }, { type: i2.NavigationService }, { type: i2.NavigatorService }]; } });
552
+
553
+ class DetailLocationComponent {
554
+ constructor(route, spaceService, router, userService, translate, navigatorService) {
555
+ this.route = route;
556
+ this.spaceService = spaceService;
557
+ this.router = router;
558
+ this.userService = userService;
559
+ this.translate = translate;
560
+ this.navigatorService = navigatorService;
561
+ this.isEditing = false;
562
+ this.menuItems = [];
563
+ this.isValidatingDpe = false;
564
+ this.gettingData = false;
565
+ }
566
+ async ngOnInit() {
567
+ this.id = this.route.snapshot.paramMap.get("id");
568
+ this.navigatorService.locationIDChange.next(this.id); // Send update for the location filter bar
569
+ this.space = await this.spaceService.getSpace(this.id);
570
+ this.setupMenuItems();
571
+ this.setGuestLink();
572
+ this.setCanArchive();
573
+ }
574
+ setupMenuItems() {
575
+ this.menuItems = [
576
+ { label: "Locations", url: "/localisation" },
577
+ {
578
+ label: this.space.name,
579
+ url: `/localisation/${this.space.id}`,
580
+ },
581
+ ];
582
+ if (this.isEditing) {
583
+ this.menuItems.push({ label: "Edit" });
584
+ }
585
+ else {
586
+ this.menuItems.push({ label: "Information" });
587
+ }
588
+ }
589
+ onEdit() {
590
+ this.isEditing = true;
591
+ this.setupMenuItems();
592
+ }
593
+ onReturn() {
594
+ // this.locationService.back()
595
+ this.router.navigate(["dashboard/localisation"]);
596
+ }
597
+ reset() {
598
+ this.isEditing = false;
599
+ this.setupMenuItems();
600
+ }
601
+ async onArchive() {
602
+ const message = this.translate.instant("confirm.archiveSpace");
603
+ if (window.confirm(message)) {
604
+ await this.spaceService.softDeleteSpace(this.id);
605
+ this.onReturn();
606
+ }
607
+ }
608
+ onFormChange(event) {
609
+ this.isEditing = false;
610
+ if (event) {
611
+ this.space = event;
612
+ }
613
+ this.setupMenuItems();
614
+ }
615
+ onDpeValidate() {
616
+ this.isValidatingDpe = true;
617
+ this.parsedMetadata = JSON.parse(this.space.metadata);
618
+ this.parsedMetadata = this.parsedMetadata.filter((result) => result.geo_adresse);
619
+ }
620
+ onSelectChange(selectedResult) {
621
+ for (const result of this.parsedMetadata) {
622
+ if (result !== selectedResult && result.selected) {
623
+ result.selected = false;
624
+ }
625
+ }
626
+ this.selectedDpeResult = selectedResult;
627
+ }
628
+ async onSaveResult() {
629
+ if (this.selectedDpeResult) {
630
+ const input = {
631
+ id: this.space.id,
632
+ constructionYear: this.selectedDpeResult.annee_construction,
633
+ classEnergyConsumption: this.selectedDpeResult.classe_consommation_energie,
634
+ classGes: this.selectedDpeResult.classe_estimation_ges,
635
+ status: SpaceStatus.DPE_VALIDATED,
636
+ };
637
+ const updatedLoc = await this.spaceService.updateSpace(input);
638
+ this.space = updatedLoc;
639
+ this.isValidatingDpe = false;
640
+ }
641
+ }
642
+ onCancel() {
643
+ this.isValidatingDpe = false;
644
+ }
645
+ async onErase() {
646
+ await this.spaceService.updateSpace({
647
+ id: this.space.id,
648
+ status: SpaceStatus.DPE_VALIDATED,
649
+ });
650
+ this.space.status = SpaceStatus.DPE_VALIDATED;
651
+ this.isValidatingDpe = false;
652
+ }
653
+ async onGetDpeData() {
654
+ this.gettingData = true;
655
+ await this.spaceService.updateSpace({
656
+ id: this.space.id,
657
+ status: SpaceStatus.DPE_FETCH,
658
+ });
659
+ // force update of location
660
+ setTimeout(async () => {
661
+ this.space = await this.spaceService.getSpace(this.space.id);
662
+ this.gettingData = false;
663
+ }, 1500);
664
+ }
665
+ setGuestLink() {
666
+ if (this.space.guestLink) {
667
+ const { origin } = document.location;
668
+ this.guestLink = `${origin}/${this.space.guestLink}`;
669
+ setTimeout(() => {
670
+ const hyperlink = document.querySelector("#linkHref");
671
+ const message = this.translate.instant("confirm.followLink");
672
+ hyperlink.addEventListener("click", async () => {
673
+ if (window.confirm(message)) {
674
+ window.location.href = this.guestLink;
675
+ await this.userService.logoutCurrentAndSignOutAuth();
676
+ this.router.navigate(["auth"]);
677
+ }
678
+ });
679
+ }, 0);
680
+ }
681
+ }
682
+ async setCanArchive() {
683
+ this.canArchive = this.userService.isSPAdmin()
684
+ ? true
685
+ : await this.spaceService.canArchiveSpace(this.id);
686
+ }
687
+ }
688
+ DetailLocationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: DetailLocationComponent, deps: [{ token: i1.ActivatedRoute }, { token: i2.SpaceService }, { token: i1.Router }, { token: i2.BaseUserService }, { token: i3.TranslateService }, { token: i2.NavigatorService }], target: i0.ɵɵFactoryTarget.Component });
689
+ DetailLocationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: DetailLocationComponent, selector: "lib-detail-location", ngImport: i0, template: "<div class=\"container-fluid\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"reset()\"></lib-tab-navigation>\n </div>\n <button class=\"btn btn-outline-primary rounded-pill me-3\" (click)=\"onReturn()\">{{'Back to locations' |\n translate}}</button>\n <div class=\"row\" *ngIf=\"!isEditing && space\">\n <div class=\"col-md-5\">\n <ul class=\"list-group list-group-flush\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate}}: {{space.name}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Address' | translate}}: {{space.addresses}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Construction year' | translate}}:\n {{space.constructionYear ?\n space.constructionYear : \"No data\" | translate}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Latitude' | translate}}:\n {{space.latitude ?\n space.latitude : \"No data\" | translate}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Longitude' | translate}}: {{space.longitude ?\n space.longitude : \"No data\" | translate}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Access level' | translate}}: {{space.level | translate\n }} </li>\n <li class=\"list-group-item bg-transparent\">{{'Energy Class' | translate}}:\n {{space.classEnergyConsumption ?\n space.classEnergyConsumption : \"No data\" | translate}}\n <button *ngIf=\"space.status !== 'DPE_TO_VALIDATE'\"\n class=\"btn btn-outline-primary rounded-pill no-lowercase ms-5\" (click)=\"onGetDpeData()\">\n {{'Get data from API DPE' | translate}}</button>\n <a class=\"bg-transparent ms-5\" *ngIf=\"space.status === 'DPE_TO_VALIDATE'\"\n (click)=\"onDpeValidate()\" style=\"cursor: pointer;\">\n <span class=\"iconify\" data-icon=\"mdi:alert-circle-check\" style=\"color: #f19c80;\" data-width=\"25\"\n data-height=\"25\"></span>\n {{'DPE data found. Click to validate' | translate}}\n </a>\n </li>\n <li class=\"list-group-item bg-transparent\">{{'Greenhouse gas emission Class' | translate}}:\n {{space.classGes ? space.classGes : \"No data\" | translate}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Shareable Guest Link' | translate}}:\n <div style=\"display: inline;\" *ngIf=\"guestLink\">\n <a id=\"linkHref\" target=\"_blank\">{{'Try the link' | translate }}</a>\n <div ngxClipboard [cbContent]=\"guestLink\" [style.cursor]=\"'pointer'\"\n style=\"margin-left: 5px; max-width: 20px; display: inline;\" ngbTooltip=\"Copied!\"\n triggers=\"click:blur\">\n <span class=\"iconify\" data-icon=\"mdi:content-copy\" data-inline=\"false\" data-width=\"20\"\n data-height=\"20\"></span>\n </div>\n </div>\n <p *ngIf=\"!guestLink\" style=\"display: inline;\">{{'No link' | translate }}</p>\n <p *ngIf=\"!guestLink\"><small>({{'Generate link from a VISITOR mission'|translate}})</small>\n </p>\n </li>\n\n </ul>\n <div class=\"d-flex justify-content-center\" *ngIf=\"gettingData\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <hr>\n <div class=\"d-flex\">\n <button class=\"btn btn-outline-primary rounded-pill me-3\" (click)=\"onEdit()\">{{'Edit' |\n translate}}</button>\n <div class=\"tooltip-wrapper\" ngbTooltip=\"{{!canArchive ? ('Contact SmarterPlan' | translate) : ('Archive location and related data' | translate)}}\">\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onArchive()\" [disabled]=\"!canArchive\">\n {{'Archive' | translate}}</button>\n </div>\n </div>\n\n\n </div>\n <div class=\"col-md-7\" *ngIf=\"isValidatingDpe\">\n <h6>{{'Most relevant results for' | translate}} : \n <b>diagnostics de performance \u00E9nerg\u00E9tique (DPE) des b\u00E2timents tertiaires</b></h6>\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">{{'Apply to location' | translate}}</th>\n <th scope=\"col\">{{'Address' | translate}}</th>\n <th scope=\"col\">{{'Activity sector' | translate}}</th>\n <th scope=\"col\">{{'Construction year' | translate}}</th>\n <th scope=\"col\">{{'Energy Class' | translate}}</th>\n <th scope=\"col\">{{'Greenhouse gas emission Class' | translate}}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let result of parsedMetadata; index as i\">\n <th scope=\"row\">{{ i + 1 }}</th>\n <td><input class=\"checkbox-apply\" type=\"checkbox\" [(ngModel)]=\"result.selected\"\n (change)=\"onSelectChange(result)\"></td>\n <td>{{result.geo_adresse}}</td>\n <td>{{result.secteur_activite}}</td>\n <td>{{ result.annee_construction }}</td>\n <td>{{ result.classe_consommation_energie }}</td>\n <td>{{ result.classe_estimation_ges }}</td>\n </tr>\n </tbody>\n </table>\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onSaveResult()\"\n [disabled]=\"!selectedDpeResult\">{{'Save selected' |\n translate}}</button>\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onErase()\">\n {{'No match (erase data)' | translate}}</button>\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onCancel()\">\n {{'Cancel' | translate}}</button>\n </div>\n </div>\n <lib-form-location *ngIf=\"isEditing\" [location]=\"space\" (onChange)=\"onFormChange($event)\"></lib-form-location>\n</div>\n", styles: [""], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }, { type: FormLocationComponent, selector: "lib-form-location", inputs: ["location"], outputs: ["onChange"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7$1.ClipboardDirective, selector: "[ngxClipboard]", inputs: ["ngxClipboard", "container", "cbContent", "cbSuccessMsg"], outputs: ["cbOnSuccess", "cbOnError"] }, { type: i7.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "triggers", "container", "disableTooltip", "tooltipClass", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "translate": i3.TranslatePipe } });
690
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: DetailLocationComponent, decorators: [{
691
+ type: Component,
692
+ args: [{ selector: 'lib-detail-location', template: "<div class=\"container-fluid\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"reset()\"></lib-tab-navigation>\n </div>\n <button class=\"btn btn-outline-primary rounded-pill me-3\" (click)=\"onReturn()\">{{'Back to locations' |\n translate}}</button>\n <div class=\"row\" *ngIf=\"!isEditing && space\">\n <div class=\"col-md-5\">\n <ul class=\"list-group list-group-flush\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate}}: {{space.name}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Address' | translate}}: {{space.addresses}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Construction year' | translate}}:\n {{space.constructionYear ?\n space.constructionYear : \"No data\" | translate}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Latitude' | translate}}:\n {{space.latitude ?\n space.latitude : \"No data\" | translate}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Longitude' | translate}}: {{space.longitude ?\n space.longitude : \"No data\" | translate}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Access level' | translate}}: {{space.level | translate\n }} </li>\n <li class=\"list-group-item bg-transparent\">{{'Energy Class' | translate}}:\n {{space.classEnergyConsumption ?\n space.classEnergyConsumption : \"No data\" | translate}}\n <button *ngIf=\"space.status !== 'DPE_TO_VALIDATE'\"\n class=\"btn btn-outline-primary rounded-pill no-lowercase ms-5\" (click)=\"onGetDpeData()\">\n {{'Get data from API DPE' | translate}}</button>\n <a class=\"bg-transparent ms-5\" *ngIf=\"space.status === 'DPE_TO_VALIDATE'\"\n (click)=\"onDpeValidate()\" style=\"cursor: pointer;\">\n <span class=\"iconify\" data-icon=\"mdi:alert-circle-check\" style=\"color: #f19c80;\" data-width=\"25\"\n data-height=\"25\"></span>\n {{'DPE data found. Click to validate' | translate}}\n </a>\n </li>\n <li class=\"list-group-item bg-transparent\">{{'Greenhouse gas emission Class' | translate}}:\n {{space.classGes ? space.classGes : \"No data\" | translate}} </li>\n <li class=\"list-group-item bg-transparent\">{{'Shareable Guest Link' | translate}}:\n <div style=\"display: inline;\" *ngIf=\"guestLink\">\n <a id=\"linkHref\" target=\"_blank\">{{'Try the link' | translate }}</a>\n <div ngxClipboard [cbContent]=\"guestLink\" [style.cursor]=\"'pointer'\"\n style=\"margin-left: 5px; max-width: 20px; display: inline;\" ngbTooltip=\"Copied!\"\n triggers=\"click:blur\">\n <span class=\"iconify\" data-icon=\"mdi:content-copy\" data-inline=\"false\" data-width=\"20\"\n data-height=\"20\"></span>\n </div>\n </div>\n <p *ngIf=\"!guestLink\" style=\"display: inline;\">{{'No link' | translate }}</p>\n <p *ngIf=\"!guestLink\"><small>({{'Generate link from a VISITOR mission'|translate}})</small>\n </p>\n </li>\n\n </ul>\n <div class=\"d-flex justify-content-center\" *ngIf=\"gettingData\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <hr>\n <div class=\"d-flex\">\n <button class=\"btn btn-outline-primary rounded-pill me-3\" (click)=\"onEdit()\">{{'Edit' |\n translate}}</button>\n <div class=\"tooltip-wrapper\" ngbTooltip=\"{{!canArchive ? ('Contact SmarterPlan' | translate) : ('Archive location and related data' | translate)}}\">\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onArchive()\" [disabled]=\"!canArchive\">\n {{'Archive' | translate}}</button>\n </div>\n </div>\n\n\n </div>\n <div class=\"col-md-7\" *ngIf=\"isValidatingDpe\">\n <h6>{{'Most relevant results for' | translate}} : \n <b>diagnostics de performance \u00E9nerg\u00E9tique (DPE) des b\u00E2timents tertiaires</b></h6>\n <table class=\"table table-striped\">\n <thead>\n <tr>\n <th scope=\"col\">#</th>\n <th scope=\"col\">{{'Apply to location' | translate}}</th>\n <th scope=\"col\">{{'Address' | translate}}</th>\n <th scope=\"col\">{{'Activity sector' | translate}}</th>\n <th scope=\"col\">{{'Construction year' | translate}}</th>\n <th scope=\"col\">{{'Energy Class' | translate}}</th>\n <th scope=\"col\">{{'Greenhouse gas emission Class' | translate}}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let result of parsedMetadata; index as i\">\n <th scope=\"row\">{{ i + 1 }}</th>\n <td><input class=\"checkbox-apply\" type=\"checkbox\" [(ngModel)]=\"result.selected\"\n (change)=\"onSelectChange(result)\"></td>\n <td>{{result.geo_adresse}}</td>\n <td>{{result.secteur_activite}}</td>\n <td>{{ result.annee_construction }}</td>\n <td>{{ result.classe_consommation_energie }}</td>\n <td>{{ result.classe_estimation_ges }}</td>\n </tr>\n </tbody>\n </table>\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onSaveResult()\"\n [disabled]=\"!selectedDpeResult\">{{'Save selected' |\n translate}}</button>\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onErase()\">\n {{'No match (erase data)' | translate}}</button>\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onCancel()\">\n {{'Cancel' | translate}}</button>\n </div>\n </div>\n <lib-form-location *ngIf=\"isEditing\" [location]=\"space\" (onChange)=\"onFormChange($event)\"></lib-form-location>\n</div>\n", styles: [""] }]
693
+ }], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i2.SpaceService }, { type: i1.Router }, { type: i2.BaseUserService }, { type: i3.TranslateService }, { type: i2.NavigatorService }]; } });
694
+
695
+ class CalibrationComponent {
696
+ constructor(matterportImportService, planService, route, router, zoneService, spaceService, navigationService, translate) {
697
+ this.matterportImportService = matterportImportService;
698
+ this.planService = planService;
699
+ this.route = route;
700
+ this.router = router;
701
+ this.zoneService = zoneService;
702
+ this.spaceService = spaceService;
703
+ this.navigationService = navigationService;
704
+ this.translate = translate;
705
+ /** Item to display for the navbar */
706
+ this.menuItems = [];
707
+ /** Zoom ratio of the canvas/plan */
708
+ this.canvasZoom = 0;
709
+ /** Opacity for matterport iframe */
710
+ this.opacity = 50;
711
+ /** Current rotation for matterport, [0-360], matterport rotation is bind to input range */
712
+ this.rotation = 180;
713
+ /** Previous rotation, use to calculate the deltaRotation to rotate, when user change rotation value using range input */
714
+ this.prevRotation = 180;
715
+ /** True when the user is currently rotating with the input range, the matterport rotation events will be ignored */
716
+ this.isRotatingRange = false;
717
+ /** Rotating queue event to send to matterport */
718
+ this.rotatingQueueEvent = [];
719
+ /** True when Vertical symmetry is enabled */
720
+ this.reflectV = false;
721
+ /** True when Horizontal symmetry is enabled */
722
+ this.reflectH = false;
723
+ this.chosenPlanIsPdf = false;
724
+ /** First Point in visit, used to determine the calibration */
725
+ this.firstMarkVisit = { x: 0, y: 0 };
726
+ /** Second Point in visit, used to determine the calibration */
727
+ this.secondMarkVisit = { x: 0.5, y: 0.5 };
728
+ /** Equivalent of 'firstMarkVisit' in canvas */
729
+ this.firstMarkPlan = { x: 0, y: 0 };
730
+ /** Equivalent of 'secondMarkVisit' in canvas */
731
+ this.secondMarkPlan = { x: 0, y: 0 };
732
+ /** Calibration result, (offsetX, offsetY, x, y) */
733
+ this.calibrage = {};
734
+ /** Is in preview mode (see scan result) */
735
+ this.preview = false;
736
+ /** Matterport is loading */
737
+ this.loading = true;
738
+ this.positions = [];
739
+ this.floors = [];
740
+ this.zoomCanBeChanged = false;
741
+ }
742
+ ngOnDestroy() {
743
+ if (!this.loading) {
744
+ this.matterportImportService.removeFrame();
745
+ }
746
+ }
747
+ async ngOnInit() {
748
+ this.spaceID = this.route.snapshot.queryParams.spaceID;
749
+ this.currentSpace = await this.spaceService.getSpace(this.spaceID);
750
+ /** Menu nav bar */
751
+ this.setupMenuItems();
752
+ this.chosenPlan = this.planService.getChosenPlan();
753
+ if (this.chosenPlan) {
754
+ this.chosenPlanIsPdf = this.chosenPlan.annexe.includes("pdf");
755
+ this.launchViewer();
756
+ setTimeout(() => {
757
+ this.configureCanvas().then(() => {
758
+ this.panzoom = panzoom(this.canvas, {
759
+ bounds: true,
760
+ boundsPadding: 0,
761
+ maxZoom: 2.5,
762
+ minZoom: 0.1,
763
+ initialZoom: 1,
764
+ zoomSpeed: 0.02,
765
+ });
766
+ });
767
+ }, 500);
768
+ }
769
+ else {
770
+ this.router.navigate([
771
+ "/dashboard/localisation",
772
+ this.spaceID,
773
+ "plans",
774
+ ]);
775
+ }
776
+ }
777
+ /** **********************************************************************************************
778
+ * MENU NAV BAR
779
+ *********************************************************************************************** */
780
+ setupMenuItems() {
781
+ this.menuItems = [
782
+ { label: "Locations", url: "/localisation" },
783
+ {
784
+ label: this.currentSpace.name,
785
+ url: `/localisation/${this.spaceID}`,
786
+ },
787
+ {
788
+ label: "Plans",
789
+ url: `/localisation/${this.currentSpace.id}/plans`,
790
+ },
791
+ {
792
+ label: "Calibration",
793
+ url: `/localisation/${this.currentSpace.id}/plan-calibration?spaceID=${this.currentSpace.id}`,
794
+ },
795
+ ];
796
+ }
797
+ /** **********************************************************************************************
798
+ * CANVAS
799
+ *********************************************************************************************** */
800
+ /** Display the plan */
801
+ async configureCanvas() {
802
+ /** Get canvas */
803
+ if (!this.canvas) {
804
+ const canvas = document.querySelector("#canvas");
805
+ this.canvas = canvas;
806
+ }
807
+ if (!this.chosenPlanIsPdf) {
808
+ const image = await getMetaForImage(this.chosenPlan.filepath);
809
+ /** Compute the new size for the canvas/matterport, base on the dimension of the plan, keeping the ratio */
810
+ const size = this.getSizeForCanvas(image.width, image.height);
811
+ /** Compute the plan zoom ratio (use for the calibration later) */
812
+ this.canvasZoom = size.w / image.width;
813
+ this.resizeView(size);
814
+ /** Draw Plan on the canvas */
815
+ this.drawImage(this.chosenPlan.filepath);
816
+ }
817
+ else {
818
+ // TODO
819
+ this.pdfToCanvas();
820
+ }
821
+ }
822
+ /** Update size of the canvas/matterport */
823
+ resizeView(size) {
824
+ this.canvasDiv.nativeElement.style.width = `${size.w}px`;
825
+ this.canvasDiv.nativeElement.style.height = `${size.h}px`;
826
+ this.canvas.style.width = `${size.w}px`;
827
+ this.canvas.style.height = `${size.h}px`;
828
+ this.canvas.width = size.w;
829
+ this.canvas.height = size.h;
830
+ this.canvasSize = size;
831
+ }
832
+ /** Draw something on the canvas */
833
+ drawImage(url, x = 0, y = 0, icone = false) {
834
+ const image = new Image();
835
+ const size = this.canvasSize;
836
+ const context = this.canvas.getContext("2d");
837
+ /** Flip the context, for symmetry */
838
+ // if (this.reflectV) {
839
+ // context.translate(size.w, 0);
840
+ // context.scale(-1, 1);
841
+ // }
842
+ // if (this.reflectH) {
843
+ // context.translate(0, size.h);
844
+ // context.scale(1, -1);
845
+ // }
846
+ let imageWidth = size.w;
847
+ let imageHeight = size.h;
848
+ image.addEventListener("load", () => {
849
+ if (icone) {
850
+ imageWidth = image.width;
851
+ imageHeight = image.height;
852
+ }
853
+ context.drawImage(image, 0, 0, image.width, image.height, x, y, imageWidth, imageHeight);
854
+ });
855
+ image.src = url;
856
+ }
857
+ /** Get the size for the canvas, based on the plan size, keeping the ratio */
858
+ getSizeForCanvas(w, h) {
859
+ let canvasW = this.canvasDiv.nativeElement.offsetWidth;
860
+ let canvasH = this.canvasDiv.nativeElement.offsetHeight;
861
+ const imageRatio = w / h;
862
+ const canvasRatio = canvasW / canvasH;
863
+ if (imageRatio > canvasRatio) {
864
+ canvasH = canvasW / imageRatio;
865
+ }
866
+ else {
867
+ canvasW = canvasH * imageRatio;
868
+ }
869
+ return { w: canvasW, h: canvasH };
870
+ }
871
+ /** [TODO] Convert a PDF to a Canvas */
872
+ async pdfToCanvas() {
873
+ const pdfjs = await import('pdfjs-dist/build/pdf');
874
+ const pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.entry');
875
+ pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
876
+ const pdf = await getDocument(this.chosenPlan.filepath).promise;
877
+ const page = await pdf.getPage(1);
878
+ const viewPortParameters = { scale: 1 };
879
+ const viewport = page.getViewport(viewPortParameters);
880
+ this.canvas.height = viewport.height;
881
+ this.canvas.width = viewport.width;
882
+ const size = this.getSizeForCanvas(viewport.width, viewport.height);
883
+ console.log(size);
884
+ this.canvasSize = size;
885
+ this.canvasZoom = size.w / viewport.width;
886
+ const context = this.canvas.getContext("2d");
887
+ const renderContext = {
888
+ canvasContext: context,
889
+ viewport,
890
+ };
891
+ const renderTask = page.render(renderContext).promise;
892
+ renderTask.then(() => console.log("Page rendered"));
893
+ // const res = canvas.toDataURL();
894
+ // if (pdf != null) pdf.destroy();
895
+ // return res;
896
+ }
897
+ /** User click to user Symmetry Vertical */
898
+ onReflectV() {
899
+ this.reflectV = !this.reflectV;
900
+ this.configureCanvas();
901
+ }
902
+ /** User click to user Symmetry Horizontal */
903
+ onReflectH() {
904
+ this.reflectH = !this.reflectH;
905
+ this.configureCanvas();
906
+ }
907
+ /** **********************************************************************************************
908
+ * MATTERPORT
909
+ *********************************************************************************************** */
910
+ /** Launch matterport */
911
+ async launchViewer() {
912
+ await this.matterportImportService.initSdkForModel(this.chosenPlan.model3d);
913
+ const { sdk } = this.matterportImportService;
914
+ this.floors = Object.values(this.matterportImportService.floors);
915
+ // console.log(this.floors);
916
+ setTimeout(async () => {
917
+ /** Move Floor mode */
918
+ await sdk.Mode.moveTo(sdk.Mode.Mode.FLOORPLAN);
919
+ /** Display the correct floor for the plan */
920
+ try {
921
+ await this.matterportImportService.moveToFloor(this.chosenPlan.name);
922
+ }
923
+ catch {
924
+ console.log("couldnot move to floor");
925
+ }
926
+ /** We can display the matterport view now */
927
+ this.loading = false;
928
+ }, 300);
929
+ /** When the user try to change the view mode we set it back to floorpan */
930
+ sdk.on(sdk.Mode.Event.CHANGE_END, (oldMode, newMode) => {
931
+ if (newMode !== sdk.Mode.Mode.FLOORPLAN) {
932
+ sdk.Mode.moveTo(sdk.Mode.Mode.FLOORPLAN, {
933
+ transition: "transition.instant",
934
+ });
935
+ }
936
+ });
937
+ /** Update current camera pose and rotation */
938
+ sdk.Camera.pose.subscribe((event) => {
939
+ /** We update only if the user is not changing the rotation with the <input> range, to not override the <input> value */
940
+ if (!this.isRotatingRange) {
941
+ this.rotation = event.rotation.y + 180;
942
+ this.prevRotation = this.rotation;
943
+ this.cameraPose = event;
944
+ }
945
+ });
946
+ }
947
+ /** When the <input> range, for rotation, change */
948
+ async onRotationChange() {
949
+ const rotationDelta = this.rotation - this.prevRotation;
950
+ this.prevRotation = this.rotation;
951
+ /** Send the rotationDelta to th queue event in order to be send to matterport */
952
+ this.rotatingQueueEvent.push(rotationDelta);
953
+ /** Start to process the queue, if it's the first event */
954
+ if (!this.isRotatingRange) {
955
+ this.isRotatingRange = true;
956
+ this.processRotatingQueueEvent();
957
+ }
958
+ }
959
+ /** Process the rotating event queue, send event one by one to matterport */
960
+ async processRotatingQueueEvent() {
961
+ const { sdk } = this.matterportImportService;
962
+ /** While the queue is not empty */
963
+ while (this.rotatingQueueEvent.length > 0) {
964
+ /** Rotate matterport by deltaRotation and remove event from queue */
965
+ await sdk.Camera.rotate(this.rotatingQueueEvent.shift(), 0, {
966
+ speed: 360,
967
+ });
968
+ }
969
+ /** When the queue is empty, then the user is not rotating with the <input> range anymore */
970
+ this.isRotatingRange = false;
971
+ }
972
+ /** **********************************************************************************************
973
+ * USER ACTIONS
974
+ *********************************************************************************************** */
975
+ /** Preview calibration */
976
+ async onPreview() {
977
+ this.preview = true;
978
+ /** Place sweeps */
979
+ this.onPlaceSweeps();
980
+ }
981
+ async calibrate() {
982
+ const [navigation] = this.currentNavigations;
983
+ const position = JSON.parse(navigation.position);
984
+ this.firstMarkPlan =
985
+ await this.matterportImportService.sdk.Renderer.getScreenPosition({
986
+ x: position.x,
987
+ y: position.y,
988
+ z: position.z,
989
+ });
990
+ this.secondMarkPlan =
991
+ await this.matterportImportService.sdk.Renderer.getScreenPosition({
992
+ x: position.x,
993
+ y: 0,
994
+ z: 0,
995
+ });
996
+ const deltaZVisit = 0 - position.z;
997
+ let deltaXPlan = this.secondMarkPlan.x - this.firstMarkPlan.x;
998
+ if (deltaXPlan === 0) {
999
+ // console.log("we moved along Y axis on Plan");
1000
+ const deltaYPlan = this.secondMarkPlan.y - this.firstMarkPlan.y;
1001
+ this.calibrage.nameYAxis = "z";
1002
+ this.calibrage.nameXAxis = "x";
1003
+ this.calibrage.offsetY = this.secondMarkPlan.y;
1004
+ this.calibrage.scaleY = deltaYPlan / deltaZVisit;
1005
+ }
1006
+ else {
1007
+ // console.log(" we moved along X axis on Plan");
1008
+ this.calibrage.nameYAxis = "x";
1009
+ this.calibrage.nameXAxis = "z";
1010
+ this.calibrage.offsetX = this.secondMarkPlan.x;
1011
+ this.calibrage.scaleX = deltaXPlan / deltaZVisit;
1012
+ }
1013
+ const thirdMarkPlan = await this.matterportImportService.sdk.Renderer.getScreenPosition({
1014
+ x: 0,
1015
+ y: 0,
1016
+ z: position.z,
1017
+ });
1018
+ const deltaXVisit = 0 - position.x;
1019
+ if (deltaXPlan === 0) {
1020
+ // console.log(" we moved along X axis on Plan");
1021
+ deltaXPlan = thirdMarkPlan.x - this.firstMarkPlan.x;
1022
+ this.calibrage.offsetX = thirdMarkPlan.x;
1023
+ this.calibrage.scaleX = deltaXPlan / deltaXVisit;
1024
+ }
1025
+ else {
1026
+ const deltaYPlan = thirdMarkPlan.y - this.firstMarkPlan.y;
1027
+ this.calibrage.offsetY = thirdMarkPlan.y;
1028
+ this.calibrage.scaleY = deltaYPlan / deltaXVisit;
1029
+ }
1030
+ /** Take considiration of pan and zoom of plan on canvas */
1031
+ const transformInfo = this.panzoom.getTransform();
1032
+ const { scale, x, y } = transformInfo;
1033
+ // console.log(transformInfo);
1034
+ this.calibrage.offsetXPlan = x;
1035
+ this.calibrage.offsetYPlan = y;
1036
+ this.calibrage.scalePlan = scale;
1037
+ console.log(this.calibrage);
1038
+ }
1039
+ /** Place sweeps */
1040
+ async onPlaceSweeps() {
1041
+ /** Display all scan */
1042
+ const zone = await this.zoneService.getZone(this.chosenPlan.zoneID);
1043
+ if (zone) {
1044
+ this.currentZone = zone;
1045
+ const navigations = await this.navigationService.getNavigationsForZone(this.currentZone);
1046
+ this.currentNavigations = navigations;
1047
+ await this.calibrate();
1048
+ if (navigations.length > 0) {
1049
+ for (const navigation of navigations) {
1050
+ const testPosition = JSON.parse(navigation.position);
1051
+ const positionX = this.calibrage.nameXAxis === "x"
1052
+ ? testPosition.x
1053
+ : testPosition.z;
1054
+ const positionY = this.calibrage.nameYAxis === "x"
1055
+ ? testPosition.x
1056
+ : testPosition.z;
1057
+ const testX = positionX * this.calibrage.scaleX +
1058
+ this.calibrage.offsetX;
1059
+ const testY = positionY * this.calibrage.scaleY +
1060
+ this.calibrage.offsetY;
1061
+ // console.log("test position GREEN", testX, testY);
1062
+ const transformedX = (testX - this.calibrage.offsetXPlan) /
1063
+ this.calibrage.scalePlan; // we are undoing the pan and zoom
1064
+ const transformedY = (testY - this.calibrage.offsetYPlan) /
1065
+ this.calibrage.scalePlan;
1066
+ this.positions.push({
1067
+ navID: navigation.id,
1068
+ planID: this.chosenPlan.id,
1069
+ x: transformedX,
1070
+ y: transformedY,
1071
+ });
1072
+ const iconSize = 30 * this.calibrage.scalePlan;
1073
+ const shift = iconSize / 2;
1074
+ this.drawImage("https://api.iconify.design/mdi:adjust.svg?color=green&height=30&width=30", transformedX - shift, transformedY - shift, true);
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1079
+ /** Save calibration */
1080
+ async onSave() {
1081
+ if (this.currentZone) {
1082
+ /** Normalize with the zoom */
1083
+ // this.calibrage.offsetX /= this.canvasZoom;
1084
+ // this.calibrage.offsetY /= this.canvasZoom;
1085
+ // this.calibrage.x /= this.canvasZoom;
1086
+ // this.calibrage.y /= this.canvasZoom;
1087
+ this.calibrage.new = true;
1088
+ this.calibrage.imgWidth = this.canvasSize.w;
1089
+ this.calibrage.imgHeight = this.canvasSize.h;
1090
+ await this.planService.updatePlan({
1091
+ id: this.chosenPlan.id,
1092
+ calibration: JSON.stringify(this.calibrage),
1093
+ });
1094
+ await Promise.all(this.positions.map(async (position) => {
1095
+ const { navID } = position;
1096
+ delete position.navID;
1097
+ const nav = this.currentNavigations.find((_nav) => _nav.id === navID);
1098
+ let positionsDataToUpdate;
1099
+ if (nav.positionOnPlan) {
1100
+ const positionOnPlan = nav.positionOnPlan.map((entry) => JSON.parse(entry));
1101
+ const positionForCurrentPlan = positionOnPlan.find((entry) => entry.planID === this.chosenPlan.id);
1102
+ if (positionForCurrentPlan) {
1103
+ positionOnPlan.splice(positionOnPlan.indexOf(positionForCurrentPlan), 1, position);
1104
+ }
1105
+ else {
1106
+ positionOnPlan.push(position);
1107
+ }
1108
+ positionsDataToUpdate = [...positionOnPlan];
1109
+ }
1110
+ else {
1111
+ positionsDataToUpdate = [position];
1112
+ }
1113
+ // console.log(positionsDataToUpdate);
1114
+ await this.navigationService.updateNavigation({
1115
+ id: navID,
1116
+ positionOnPlan: positionsDataToUpdate.map((entry) => JSON.stringify(entry)),
1117
+ });
1118
+ }));
1119
+ }
1120
+ this.matterportImportService.removeFrame();
1121
+ this.router.navigate([
1122
+ "/dashboard/localisation",
1123
+ this.spaceID,
1124
+ "plans",
1125
+ ]);
1126
+ alert(this.translate.instant("calibration.success"));
1127
+ }
1128
+ /** Reset the canvas */
1129
+ onReset() {
1130
+ this.reflectH = false;
1131
+ this.reflectV = false;
1132
+ this.preview = false;
1133
+ this.configureCanvas();
1134
+ }
1135
+ /** Cancel, leave */
1136
+ onCancel() {
1137
+ this.matterportImportService.removeFrame();
1138
+ this.router.navigate([
1139
+ "/dashboard/localisation",
1140
+ this.spaceID,
1141
+ "plans",
1142
+ ]);
1143
+ alert(this.translate.instant("calibration.canceled"));
1144
+ }
1145
+ async onFloorClick(floor) {
1146
+ this.currentFloor = floor;
1147
+ await this.matterportImportService.moveToFloor(floor.id);
1148
+ }
1149
+ onActivateZoomChange() {
1150
+ this.zoomCanBeChanged = !this.zoomCanBeChanged;
1151
+ const iframe = document.querySelector("#viewer-frame");
1152
+ if (iframe) {
1153
+ iframe.style.zIndex = this.zoomCanBeChanged ? "1" : "0";
1154
+ }
1155
+ }
1156
+ }
1157
+ CalibrationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: CalibrationComponent, deps: [{ token: i2.MatterportImportService }, { token: i2.PlanService }, { token: i1.ActivatedRoute }, { token: i1.Router }, { token: i2.ZoneService }, { token: i2.SpaceService }, { token: i2.NavigationService }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
1158
+ CalibrationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: CalibrationComponent, selector: "lib-calibration", viewQueries: [{ propertyName: "canvasDiv", first: true, predicate: ["canvasDiv"], descendants: true }], ngImport: i0, template: "<div class=\"row mt-3\">\n <div class=\"col\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems'></lib-tab-navigation>\n </div>\n </div>\n</div>\n\n<div id=\"rowCanvas\">\n <div #canvasDiv id=\"canvasDiv\">\n\n <div id=\"viewer-frame\" [style.pointer-events]=\"zoomCanBeChanged?'auto':'none'\">\n <!-- <div class=\"blur\"></div> -->\n </div>\n <canvas id=\"canvas\" [ngStyle]=\"{'opacity': (preview||loading) ? '1' : 'calc(' + opacity + '/ 100)'}\"></canvas>\n </div>\n\n <div class=\"calibration-tools container-fluid\" *ngIf=\"!(preview || loading)\">\n <h1>{{'Calibration tool' | translate}}</h1>\n <div class=\"tool\">\n <div class=\"row ms-0 me-0 legend\">\n <div [ngStyle]=\"{'opacity': 'calc(' + (-(opacity-100)+10) + '/ 100)'}\">{{'Less' | translate}}</div>\n <div class=\"legend-title\">{{'Plan Opacity' | translate }}</div>\n <div [ngStyle]=\"{'opacity': 'calc(' + (opacity+10) + '/ 100)'}\">{{'More' | translate}}</div>\n </div>\n <div class=\"row ms-0 me-0 \">\n <input [disabled]=\"false\" [ngModelOptions]=\"{standalone: true}\" [(ngModel)]=\"opacity\" type=\"range\"\n class=\"form-range\" min=\"0\" max=\"100\" value=\"50\" id=\"opacity\">\n </div>\n </div>\n <div class=\"tool\">\n <div class=\"row ms-0 me-0 \">\n <div class=\"label-legend\">{{'Change Visit Floor' | translate}}</div>\n <div ngbDropdown class=\"d-inline-block ms-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>\n {{currentFloor ? currentFloor.id : ('Floor' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let floor of floors\" (click)=\"onFloorClick(floor)\">\n {{ floor.name.length > 0 ? floor.name : ('Floor ' + floor.id) }}\n </button>\n </div>\n </div>\n </div>\n\n </div>\n <div class=\"tool\">\n <div class=\"row ms-0 me-0 \">\n <div class=\"label-legend me-3\">{{'Pan/Zoom Visit' | translate}} {{zoomCanBeChanged ? \"ON\" : \"OFF\"}}\n </div>\n <input type=\"checkbox\" (change)=\"onActivateZoomChange()\" style=\"margin-top: 5px;\">\n <div class=\"card border-warning mb-3 mt-3\" *ngIf=\"zoomCanBeChanged\">\n <div class=\"card-body text-warning\">\n <h5 class=\"card-title\">{{\"calibration.wait-message\" | translate}}</h5>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"action\">\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onPreview()\"\n [disabled]=\"zoomCanBeChanged\">{{'Preview' | translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onCancel()\">{{'Cancel' |\n translate}}</button>\n\n </div>\n\n </div>\n <div class=\"calibration-tools container-fluid\" *ngIf=\"preview\">\n <h1>{{'Validation' | translate}}</h1>\n <div class=\"action\">\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSave()\">{{'Validate' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onReset()\">{{'Reset' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onCancel()\">{{'Cancel' |\n translate}}</button>\n\n </div>\n </div>\n\n\n <div class=\"calibration-tools container-fluid\" *ngIf=\"loading\">\n <h3>{{\"Waiting for Matterport\" | translate}}...</h3>\n <div class=\"d-flex justify-content-center\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n\n </div>\n</div>", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}#rowCanvas{display:flex}#canvasDiv{height:100%;width:100%;position:relative}#canvas{width:100%;height:100%;opacity:.5}#viewer-frame{top:0;position:absolute;width:100%;height:100%}.calibration-tools{margin-left:20px;width:400px;background-color:#fff;z-index:11;border:1px solid var(--smarterplan-primary);padding:7px}.form-range{width:100%}.tool{margin-top:20px}.tool .legend{justify-content:space-between;position:relative;margin-bottom:5px}.tool .legend-title{font-size:1.2rem;color:var(--smarterplan-primary);position:absolute;margin:auto;width:100%;text-align:center;font-weight:700}.tool .label-legend{color:var(--smarterplan-primary);font-size:1.1rem}.blur{width:150px;height:52px;position:absolute;background-color:#1e2023;bottom:0}.symmetry{padding:5px;border:solid 2px;border-radius:10px;color:var(--smarterplan-primary);margin:25px 10px;cursor:pointer}.symmetry:hover{color:var(--smarterplan-primary)}.symmetry-row{justify-content:center}.action{margin-top:30px;display:flex;justify-content:space-evenly}h1{color:var(--smarterplan-primary);text-align:center;margin-bottom:20px}h3{color:var(--smarterplan-primary);text-align:center;margin-bottom:10px}.btn-outline-primary.disabled{background-color:var(--smarterplan-orange)!important}\n"], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }], directives: [{ type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$2.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { type: i7.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { type: i7.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { type: i7.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["disabled"] }], pipes: { "translate": i3.TranslatePipe } });
1159
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: CalibrationComponent, decorators: [{
1160
+ type: Component,
1161
+ args: [{ selector: 'lib-calibration', template: "<div class=\"row mt-3\">\n <div class=\"col\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems'></lib-tab-navigation>\n </div>\n </div>\n</div>\n\n<div id=\"rowCanvas\">\n <div #canvasDiv id=\"canvasDiv\">\n\n <div id=\"viewer-frame\" [style.pointer-events]=\"zoomCanBeChanged?'auto':'none'\">\n <!-- <div class=\"blur\"></div> -->\n </div>\n <canvas id=\"canvas\" [ngStyle]=\"{'opacity': (preview||loading) ? '1' : 'calc(' + opacity + '/ 100)'}\"></canvas>\n </div>\n\n <div class=\"calibration-tools container-fluid\" *ngIf=\"!(preview || loading)\">\n <h1>{{'Calibration tool' | translate}}</h1>\n <div class=\"tool\">\n <div class=\"row ms-0 me-0 legend\">\n <div [ngStyle]=\"{'opacity': 'calc(' + (-(opacity-100)+10) + '/ 100)'}\">{{'Less' | translate}}</div>\n <div class=\"legend-title\">{{'Plan Opacity' | translate }}</div>\n <div [ngStyle]=\"{'opacity': 'calc(' + (opacity+10) + '/ 100)'}\">{{'More' | translate}}</div>\n </div>\n <div class=\"row ms-0 me-0 \">\n <input [disabled]=\"false\" [ngModelOptions]=\"{standalone: true}\" [(ngModel)]=\"opacity\" type=\"range\"\n class=\"form-range\" min=\"0\" max=\"100\" value=\"50\" id=\"opacity\">\n </div>\n </div>\n <div class=\"tool\">\n <div class=\"row ms-0 me-0 \">\n <div class=\"label-legend\">{{'Change Visit Floor' | translate}}</div>\n <div ngbDropdown class=\"d-inline-block ms-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>\n {{currentFloor ? currentFloor.id : ('Floor' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let floor of floors\" (click)=\"onFloorClick(floor)\">\n {{ floor.name.length > 0 ? floor.name : ('Floor ' + floor.id) }}\n </button>\n </div>\n </div>\n </div>\n\n </div>\n <div class=\"tool\">\n <div class=\"row ms-0 me-0 \">\n <div class=\"label-legend me-3\">{{'Pan/Zoom Visit' | translate}} {{zoomCanBeChanged ? \"ON\" : \"OFF\"}}\n </div>\n <input type=\"checkbox\" (change)=\"onActivateZoomChange()\" style=\"margin-top: 5px;\">\n <div class=\"card border-warning mb-3 mt-3\" *ngIf=\"zoomCanBeChanged\">\n <div class=\"card-body text-warning\">\n <h5 class=\"card-title\">{{\"calibration.wait-message\" | translate}}</h5>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"action\">\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onPreview()\"\n [disabled]=\"zoomCanBeChanged\">{{'Preview' | translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onCancel()\">{{'Cancel' |\n translate}}</button>\n\n </div>\n\n </div>\n <div class=\"calibration-tools container-fluid\" *ngIf=\"preview\">\n <h1>{{'Validation' | translate}}</h1>\n <div class=\"action\">\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSave()\">{{'Validate' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onReset()\">{{'Reset' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\" (click)=\"onCancel()\">{{'Cancel' |\n translate}}</button>\n\n </div>\n </div>\n\n\n <div class=\"calibration-tools container-fluid\" *ngIf=\"loading\">\n <h3>{{\"Waiting for Matterport\" | translate}}...</h3>\n <div class=\"d-flex justify-content-center\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n\n </div>\n</div>", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}#rowCanvas{display:flex}#canvasDiv{height:100%;width:100%;position:relative}#canvas{width:100%;height:100%;opacity:.5}#viewer-frame{top:0;position:absolute;width:100%;height:100%}.calibration-tools{margin-left:20px;width:400px;background-color:#fff;z-index:11;border:1px solid var(--smarterplan-primary);padding:7px}.form-range{width:100%}.tool{margin-top:20px}.tool .legend{justify-content:space-between;position:relative;margin-bottom:5px}.tool .legend-title{font-size:1.2rem;color:var(--smarterplan-primary);position:absolute;margin:auto;width:100%;text-align:center;font-weight:700}.tool .label-legend{color:var(--smarterplan-primary);font-size:1.1rem}.blur{width:150px;height:52px;position:absolute;background-color:#1e2023;bottom:0}.symmetry{padding:5px;border:solid 2px;border-radius:10px;color:var(--smarterplan-primary);margin:25px 10px;cursor:pointer}.symmetry:hover{color:var(--smarterplan-primary)}.symmetry-row{justify-content:center}.action{margin-top:30px;display:flex;justify-content:space-evenly}h1{color:var(--smarterplan-primary);text-align:center;margin-bottom:20px}h3{color:var(--smarterplan-primary);text-align:center;margin-bottom:10px}.btn-outline-primary.disabled{background-color:var(--smarterplan-orange)!important}\n"] }]
1162
+ }], ctorParameters: function () { return [{ type: i2.MatterportImportService }, { type: i2.PlanService }, { type: i1.ActivatedRoute }, { type: i1.Router }, { type: i2.ZoneService }, { type: i2.SpaceService }, { type: i2.NavigationService }, { type: i3.TranslateService }]; }, propDecorators: { canvasDiv: [{
1163
+ type: ViewChild,
1164
+ args: ["canvasDiv"]
1165
+ }] } });
1166
+
1167
+ class PlansComponent {
1168
+ constructor(route, router, spaceService, planService, fb, translate) {
1169
+ this.route = route;
1170
+ this.router = router;
1171
+ this.spaceService = spaceService;
1172
+ this.planService = planService;
1173
+ this.fb = fb;
1174
+ this.translate = translate;
1175
+ // chosenPlan: Plan;
1176
+ this.indexDetails = -1;
1177
+ this.loading = false;
1178
+ this.uploadingPlan = false;
1179
+ this.isCurrentPlanForZone = false;
1180
+ this.chosenPlanIsPdf = false;
1181
+ this.menuItems = [];
1182
+ }
1183
+ ngOnInit() {
1184
+ this.spaceID = this.route.snapshot.paramMap.get("id");
1185
+ this.getPlans();
1186
+ if (this.planService.getPlanFileCache() &&
1187
+ this.planService.getChosenPlan()) {
1188
+ this.addPlanFromCache(this.planService.getPlanFileCache(), this.planService.getChosenPlan());
1189
+ this.planService.setPlanFileCache(null);
1190
+ }
1191
+ this.planService.setChosenPlan(null);
1192
+ }
1193
+ setupMenuItems() {
1194
+ this.menuItems = [
1195
+ { label: "Locations", url: "/localisation" },
1196
+ {
1197
+ label: this.currentSpace.name,
1198
+ url: `/localisation/${this.currentSpace.id}`,
1199
+ },
1200
+ {
1201
+ label: "Plans",
1202
+ url: `/localisation/${this.currentSpace.id}/plans`,
1203
+ },
1204
+ ];
1205
+ }
1206
+ onGoBack() { }
1207
+ async onPlanClick(plan) {
1208
+ if (plan)
1209
+ this.planService.setChosenPlan(plan);
1210
+ this.chosenPlanIsPdf = plan.annexe.includes("pdf");
1211
+ if (!this.chosenPlanIsPdf) {
1212
+ setTimeout(() => {
1213
+ this.configureCanvas().then(() => {
1214
+ this.panzoom = panzoom(this.canvas, {
1215
+ bounds: true,
1216
+ boundsPadding: 0,
1217
+ maxZoom: 3.5,
1218
+ minZoom: 0.1,
1219
+ initialZoom: 0.5,
1220
+ });
1221
+ });
1222
+ }, 500);
1223
+ }
1224
+ }
1225
+ async configureCanvas() {
1226
+ this.uploadingPlan = true;
1227
+ const canvas = document.querySelector("#canvas");
1228
+ const image = await getMetaForImage(this.getChoosenPlan().filepath);
1229
+ canvas.width = image.width;
1230
+ canvas.height = image.height;
1231
+ this.canvas = canvas;
1232
+ this.drawImage(this.getChoosenPlan().filepath);
1233
+ this.uploadingPlan = false;
1234
+ }
1235
+ drawImage(url, x = 0, y = 0) {
1236
+ const image = new Image();
1237
+ const context = this.canvas.getContext("2d");
1238
+ const imageWidth = this.canvas.width;
1239
+ const imageHeight = this.canvas.height;
1240
+ image.addEventListener("load", () => {
1241
+ context.drawImage(image, 0, 0, image.width, image.height, x, y, imageWidth, imageHeight);
1242
+ });
1243
+ image.src = url;
1244
+ // image.crossOrigin = "*"; //need to download as png,
1245
+ // BUT: https://stackoverflow.com/questions/49503171/the-image-tag-with-crossorigin-anonymous-cant-load-success-from-s3
1246
+ }
1247
+ async getPlans() {
1248
+ this.loading = true;
1249
+ this.currentSpace = await this.spaceService.getSpace(this.spaceID);
1250
+ this.setupMenuItems();
1251
+ this.zones = this.currentSpace.zones.items;
1252
+ this.plans = await this.planService.getSingedPlansForSpace(this.spaceID);
1253
+ this.loading = false;
1254
+ if (this.plans.length === 1) {
1255
+ this.onPlanClick(this.plans[0]);
1256
+ }
1257
+ }
1258
+ getChoosenPlan() {
1259
+ return this.planService.getChosenPlan();
1260
+ }
1261
+ removeImage() {
1262
+ const context = this.canvas.getContext("2d");
1263
+ context.clearRect(0, 0, 4096, 4096);
1264
+ }
1265
+ /**
1266
+ *
1267
+ */
1268
+ onCalibrateClick() {
1269
+ if (this.getChoosenPlan()) {
1270
+ this.router.navigate(["/dashboard/localisation", this.spaceID, "plan-calibration"], { queryParams: { spaceID: this.spaceID } });
1271
+ }
1272
+ }
1273
+ /**
1274
+ *
1275
+ */
1276
+ onEditClick() {
1277
+ if (this.getChoosenPlan()) {
1278
+ this.router.navigate(["/dashboard/localisation", this.spaceID, "plan-edit"], { queryParams: { spaceID: this.spaceID } });
1279
+ }
1280
+ }
1281
+ onUploadClick() {
1282
+ const inputFile = document.querySelector("#upload-file");
1283
+ inputFile.click();
1284
+ }
1285
+ addPlan(target) {
1286
+ this.fileToUpload = target.files[0];
1287
+ this.planForm = this.fb.group({
1288
+ name: [this.fileToUpload.name, [Validators.required]],
1289
+ zoneID: [null, [Validators.required]],
1290
+ spaceID: this.spaceID,
1291
+ isModified: false,
1292
+ calibration: "",
1293
+ });
1294
+ }
1295
+ addPlanFromCache(file, plan) {
1296
+ this.fileToUpload = file;
1297
+ this.planForm = this.fb.group({
1298
+ name: [this.fileToUpload.name, [Validators.required]],
1299
+ zoneID: [plan ? plan.zoneID : null, [Validators.required]],
1300
+ spaceID: this.spaceID,
1301
+ isModified: !!plan,
1302
+ calibration: plan && plan.isModified && plan.calibration
1303
+ ? plan.calibration
1304
+ : "",
1305
+ });
1306
+ }
1307
+ async onSavePlan() {
1308
+ const formValue = this.planForm.value;
1309
+ formValue.isCurrentForZone = this.isCurrentPlanForZone;
1310
+ const createdPlan = await this.planService.createPlanWithAnnexe(formValue, this.fileToUpload);
1311
+ const filepath = await getSignedFile(createdPlan.annexe);
1312
+ if (filepath) {
1313
+ createdPlan.filepath = filepath;
1314
+ }
1315
+ if (formValue.isCurrentForZone) {
1316
+ // set all other plans to not current
1317
+ this.planService.setAllPlansForZoneNotCurrent(createdPlan.zoneID, createdPlan.id);
1318
+ }
1319
+ this.plans.push(createdPlan);
1320
+ this.planForm = null;
1321
+ this.fileToUpload = null;
1322
+ }
1323
+ onCancelUpload() {
1324
+ this.fileToUpload = null;
1325
+ this.planForm = null;
1326
+ }
1327
+ async onCurrentPlanClick() {
1328
+ await this.planService.updatePlan({
1329
+ id: this.getChoosenPlan().id,
1330
+ isCurrentForZone: this.getChoosenPlan().isCurrentForZone,
1331
+ });
1332
+ if (this.getChoosenPlan().isCurrentForZone) {
1333
+ this.planService.setAllPlansForZoneNotCurrent(this.getChoosenPlan().zoneID, this.getChoosenPlan().id);
1334
+ }
1335
+ }
1336
+ async onDownloadClick() {
1337
+ const signedFile = await downloadFileAsObject(this.getChoosenPlan().annexe);
1338
+ if (signedFile) {
1339
+ // @ts-ignore
1340
+ downloadBlob(signedFile.Body, this.getChoosenPlan().name);
1341
+ }
1342
+ }
1343
+ async onDownloadAsPng() {
1344
+ const canvas = document.querySelector("#canvas");
1345
+ canvas.toBlob(function (blob) {
1346
+ downloadBlob(blob, "plan-as-png.png");
1347
+ });
1348
+ }
1349
+ async onRemoveClick() {
1350
+ const message = this.translate.instant("confirm.deletePlan");
1351
+ if (window.confirm(message)) {
1352
+ await this.planService.deletePlan(this.getChoosenPlan());
1353
+ this.removeImage();
1354
+ this.planService.setChosenPlan(null);
1355
+ this.getPlans();
1356
+ }
1357
+ }
1358
+ }
1359
+ PlansComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PlansComponent, deps: [{ token: i1.ActivatedRoute }, { token: i1.Router }, { token: i2.SpaceService }, { token: i2.PlanService }, { token: i1$2.FormBuilder }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
1360
+ PlansComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: PlansComponent, selector: "lib-plans", ngImport: i0, template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\" class=\"dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <div class=\"mt-3\" *ngIf=\"!plans && !loading\">\n <h4 style=\"font-weight: bold;\">{{'No imported plans' | translate }}</h4>\n <p>{{'For import from 3D visit: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <div class=\"row ms-1\">\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{getChoosenPlan() ?\n getChoosenPlan().name: ('Choose Plan' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\">{{plan.name }}\n </button>\n </div>\n </div>\n <div class=\"d-inline-block\" ngbDropdown #myDrop=\"ngbDropdown\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownManual\" ngbDropdownToggle>{{'Choose action' |\n translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownManual\">\n <button (click)=\"onCalibrateClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Calibrate' |\n translate}}</button>\n <button (click)=\"onEditClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Edit plan' |\n translate}}\n </button>\n <button (click)=\"onUploadClick()\" ngbDropdownItem>{{'Upload new plan' | translate}}</button>\n <button (click)=\"onDownloadClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Download plan' |\n translate}}</button>\n <button (click)=\"onRemoveClick()\" ngbDropdownItem\n [disabled]=\"!getChoosenPlan() || getChoosenPlan().isImportedMatterport\">{{'Delete plan' |\n translate}}</button>\n <!-- <button (click)=\"onDownloadAsPng()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">\n {{'Download plan as PNG' | translate}}</button> -->\n </div>\n </div>\n </div>\n <div class=\"row mt-3 ms-0\">\n <h4 *ngIf=\"fileToUpload\">{{'New plan' | translate }}</h4>\n <input class=\"hidden\" type=\"file\" id=\"upload-file\" name=\"upload-file\" accept=\"image/png, image/jpeg, .pdf, .svg\"\n ngf-max-size=\"6MB\" (change)=\"addPlan($event.target)\">\n </div>\n <div class=\"col-md-6 mt-3\" *ngIf=\"planForm\">\n <form (ngSubmit)=\"onSavePlan()\" [formGroup]=\"planForm\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Name' | translate}} </label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Zone' | translate}} </label>\n <div class=\"col-sm-9\">\n <select class=\"form-control\" formControlName=\"zoneID\">\n <option *ngFor=\"let zone of zones\" [ngValue]=\"zone.id\">\n {{ zone.name }}\n </option>\n </select>\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Set as current plan for zone' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"isCurrentPlanForZone\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n <button [disabled]=\"planForm.invalid\" type='submit' class=\"btn btn-label-file rounded-pill\">{{'Save' |\n translate}}</button>\n <button class=\"btn btn-label-file rounded-pill ms-3\" type=\"button\" (click)=\"onCancelUpload()\">{{'Cancel' |\n translate}}</button>\n </form>\n </div>\n <ul class=\"col-md-6 list-group list-group-flush\" *ngIf=\"getChoosenPlan()\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate }} : {{getChoosenPlan().name}} </li>\n <li class=\"list-group-item bg-transparent\"> {{'Plan is ' | translate }} :\n {{getChoosenPlan().calibration ? ('Calibrated' | translate) : ('Not calibrated' | translate)}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Attributed to zone ' | translate }} :\n {{getChoosenPlan().zone ? getChoosenPlan().zone.name : 'None'}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Is current plan for zone ' | translate }} :\n <input type=\"checkbox\" [(ngModel)]=\"getChoosenPlan().isCurrentForZone\" (change)=\"onCurrentPlanClick()\">\n </li>\n\n </ul>\n\n <div class=\"row mt-4\">\n <div class=\"col-md-10\">\n <embed *ngIf=\"getChoosenPlan() && chosenPlanIsPdf\" [src]=\"getChoosenPlan().filepath | safeUrl\"\n type=\"application/pdf\" frameBorder=\"0\" scrolling=\"auto\" height=\"650px;\" width=\"100%\" />\n <div *ngIf=\"!chosenPlanIsPdf\" class=\"row\" style=\"height: 500px; overflow: hidden;\" id=\"canvasDiv\">\n <canvas id=\"canvas\" width=\"4096px\" height=\"4096px\">\n <!-- <img *ngIf=\"chosenPlan\" id=\"plan-image\" [src]=\"chosenPlan.filepath\" style=\"width: 100%;\"> -->\n </canvas>\n\n </div>\n </div>\n </div>\n</div>\n", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}.hidden{visibility:hidden;width:1px;height:1px}\n"], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { type: i7.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { type: i7.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["disabled"] }, { type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "translate": i3.TranslatePipe, "safeUrl": i2.SafeUrlPipe } });
1361
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: PlansComponent, decorators: [{
1362
+ type: Component,
1363
+ args: [{ selector: 'lib-plans', template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\" class=\"dashboard-tab\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n <div class=\"mt-3\" *ngIf=\"!plans && !loading\">\n <h4 style=\"font-weight: bold;\">{{'No imported plans' | translate }}</h4>\n <p>{{'For import from 3D visit: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <div class=\"row ms-1\">\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{getChoosenPlan() ?\n getChoosenPlan().name: ('Choose Plan' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\">{{plan.name }}\n </button>\n </div>\n </div>\n <div class=\"d-inline-block\" ngbDropdown #myDrop=\"ngbDropdown\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownManual\" ngbDropdownToggle>{{'Choose action' |\n translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownManual\">\n <button (click)=\"onCalibrateClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Calibrate' |\n translate}}</button>\n <button (click)=\"onEditClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Edit plan' |\n translate}}\n </button>\n <button (click)=\"onUploadClick()\" ngbDropdownItem>{{'Upload new plan' | translate}}</button>\n <button (click)=\"onDownloadClick()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">{{'Download plan' |\n translate}}</button>\n <button (click)=\"onRemoveClick()\" ngbDropdownItem\n [disabled]=\"!getChoosenPlan() || getChoosenPlan().isImportedMatterport\">{{'Delete plan' |\n translate}}</button>\n <!-- <button (click)=\"onDownloadAsPng()\" ngbDropdownItem [disabled]=\"!getChoosenPlan()\">\n {{'Download plan as PNG' | translate}}</button> -->\n </div>\n </div>\n </div>\n <div class=\"row mt-3 ms-0\">\n <h4 *ngIf=\"fileToUpload\">{{'New plan' | translate }}</h4>\n <input class=\"hidden\" type=\"file\" id=\"upload-file\" name=\"upload-file\" accept=\"image/png, image/jpeg, .pdf, .svg\"\n ngf-max-size=\"6MB\" (change)=\"addPlan($event.target)\">\n </div>\n <div class=\"col-md-6 mt-3\" *ngIf=\"planForm\">\n <form (ngSubmit)=\"onSavePlan()\" [formGroup]=\"planForm\">\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Name' | translate}} </label>\n <div class=\"col-sm-9\">\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Zone' | translate}} </label>\n <div class=\"col-sm-9\">\n <select class=\"form-control\" formControlName=\"zoneID\">\n <option *ngFor=\"let zone of zones\" [ngValue]=\"zone.id\">\n {{ zone.name }}\n </option>\n </select>\n </div>\n </div>\n <div class=\"mb-3 row\">\n <label class=\"col-sm-3\">{{'Set as current plan for zone' | translate}}</label>\n <div class=\"col-sm-3\">\n <input type=\"checkbox\" [(ngModel)]=\"isCurrentPlanForZone\" [ngModelOptions]=\"{standalone: true}\">\n </div>\n </div>\n <button [disabled]=\"planForm.invalid\" type='submit' class=\"btn btn-label-file rounded-pill\">{{'Save' |\n translate}}</button>\n <button class=\"btn btn-label-file rounded-pill ms-3\" type=\"button\" (click)=\"onCancelUpload()\">{{'Cancel' |\n translate}}</button>\n </form>\n </div>\n <ul class=\"col-md-6 list-group list-group-flush\" *ngIf=\"getChoosenPlan()\">\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate }} : {{getChoosenPlan().name}} </li>\n <li class=\"list-group-item bg-transparent\"> {{'Plan is ' | translate }} :\n {{getChoosenPlan().calibration ? ('Calibrated' | translate) : ('Not calibrated' | translate)}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Attributed to zone ' | translate }} :\n {{getChoosenPlan().zone ? getChoosenPlan().zone.name : 'None'}}</li>\n <li class=\"list-group-item bg-transparent\">{{'Is current plan for zone ' | translate }} :\n <input type=\"checkbox\" [(ngModel)]=\"getChoosenPlan().isCurrentForZone\" (change)=\"onCurrentPlanClick()\">\n </li>\n\n </ul>\n\n <div class=\"row mt-4\">\n <div class=\"col-md-10\">\n <embed *ngIf=\"getChoosenPlan() && chosenPlanIsPdf\" [src]=\"getChoosenPlan().filepath | safeUrl\"\n type=\"application/pdf\" frameBorder=\"0\" scrolling=\"auto\" height=\"650px;\" width=\"100%\" />\n <div *ngIf=\"!chosenPlanIsPdf\" class=\"row\" style=\"height: 500px; overflow: hidden;\" id=\"canvasDiv\">\n <canvas id=\"canvas\" width=\"4096px\" height=\"4096px\">\n <!-- <img *ngIf=\"chosenPlan\" id=\"plan-image\" [src]=\"chosenPlan.filepath\" style=\"width: 100%;\"> -->\n </canvas>\n\n </div>\n </div>\n </div>\n</div>\n", styles: [".button-visit{display:none;height:30px;width:30px;position:absolute;background:url(https://api.iconify.design/mdi:map-marker-check.svg?color=red&height=30) no-repeat scroll 0 0 transparent;border:none}#button-visit-left{left:350px;top:550px}#button-visit-right{left:500px;top:600px}.hidden{visibility:hidden;width:1px;height:1px}\n"] }]
1364
+ }], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i1.Router }, { type: i2.SpaceService }, { type: i2.PlanService }, { type: i1$2.FormBuilder }, { type: i3.TranslateService }]; } });
1365
+
1366
+ class ChevronComponent {
1367
+ constructor() {
1368
+ this.conditionShowing = false;
1369
+ }
1370
+ ngOnInit() {
1371
+ }
1372
+ }
1373
+ ChevronComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ChevronComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1374
+ ChevronComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: ChevronComponent, selector: "lib-chevron", inputs: { conditionShowing: "conditionShowing" }, ngImport: i0, template: "<div style=\"height: 30px; width: 30px;\">\n <span class=\"icon-container\" >\n <div [class]=\"conditionShowing ? 'icon-img-up' : 'icon-img-down'\"></div>\n </span>\n</div>\n", styles: [".icon-container{height:30px;width:30px}.icon-img-down{width:30px;height:100%;background:url(https://api.iconify.design/mdi-light/chevron-right.svg?width=28) no-repeat center center/contain}.icon-img-up{width:30px;height:100%;background:url(https://api.iconify.design/mdi-light/chevron-down.svg?width=28) no-repeat center center/contain}\n"] });
1375
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ChevronComponent, decorators: [{
1376
+ type: Component,
1377
+ args: [{ selector: 'lib-chevron', template: "<div style=\"height: 30px; width: 30px;\">\n <span class=\"icon-container\" >\n <div [class]=\"conditionShowing ? 'icon-img-up' : 'icon-img-down'\"></div>\n </span>\n</div>\n", styles: [".icon-container{height:30px;width:30px}.icon-img-down{width:30px;height:100%;background:url(https://api.iconify.design/mdi-light/chevron-right.svg?width=28) no-repeat center center/contain}.icon-img-up{width:30px;height:100%;background:url(https://api.iconify.design/mdi-light/chevron-down.svg?width=28) no-repeat center center/contain}\n"] }]
1378
+ }], ctorParameters: function () { return []; }, propDecorators: { conditionShowing: [{
1379
+ type: Input
1380
+ }] } });
1381
+
1382
+ class VisitsComponent {
1383
+ constructor(fb, matterportImportService, captureService, nodeService, spaceService, route, modalService, visitService) {
1384
+ this.fb = fb;
1385
+ this.matterportImportService = matterportImportService;
1386
+ this.captureService = captureService;
1387
+ this.nodeService = nodeService;
1388
+ this.spaceService = spaceService;
1389
+ this.route = route;
1390
+ this.modalService = modalService;
1391
+ this.visitService = visitService;
1392
+ this.visitIndexDetails = -1;
1393
+ this.isEditing = false;
1394
+ this.visitSubmitted = false;
1395
+ this.currentSpace = { zones: [] };
1396
+ this.visits = [];
1397
+ this.canCancelImport = false;
1398
+ this.imagesProcessed = 0;
1399
+ this.totalImages = 0;
1400
+ this.menuItems = [];
1401
+ this.matterportImportService.importingImages.subscribe(() => {
1402
+ this.canCancelImport = true;
1403
+ });
1404
+ this.matterportImportService.sweepProcessedCount.subscribe((count) => {
1405
+ this.imagesProcessed = count + 1;
1406
+ });
1407
+ this.matterportImportService.totalSweepsCount.subscribe((count) => {
1408
+ this.totalImages = count;
1409
+ });
1410
+ }
1411
+ ngOnInit() {
1412
+ this.spaceId = this.route.snapshot.paramMap.get("id");
1413
+ this.resetVisits();
1414
+ }
1415
+ /*
1416
+ Visit = {
1417
+ captureID: string,
1418
+ model3d: string,
1419
+ takenAt: number,
1420
+ spaceID: string,
1421
+ viewer: CaptureViewer,
1422
+ surface: number,
1423
+ node: Node,
1424
+ name?: string | null
1425
+ }
1426
+ */
1427
+ setupMenuItems() {
1428
+ this.menuItems = [
1429
+ { label: "Locations", url: "/localisation" },
1430
+ {
1431
+ label: this.currentSpace.name,
1432
+ url: `/localisation/${this.currentSpace.id}`,
1433
+ },
1434
+ {
1435
+ label: "Virtual visits",
1436
+ url: `/localisation/${this.currentSpace.id}/visits`,
1437
+ },
1438
+ ];
1439
+ }
1440
+ onGoBack() {
1441
+ this.onCancel();
1442
+ }
1443
+ async resetVisits() {
1444
+ this.visits = [];
1445
+ this.currentSpace = await this.spaceService.getSpace(this.spaceId);
1446
+ this.setupMenuItems();
1447
+ await this.visitService
1448
+ .getVisitsForSpaceForUser(this.spaceId)
1449
+ .then((visits) => {
1450
+ if (visits) {
1451
+ this.visits = visits;
1452
+ }
1453
+ });
1454
+ }
1455
+ initVisitForm(input = null) {
1456
+ if (input) {
1457
+ this.visitForm = this.fb.group({
1458
+ captureID: input.captureID,
1459
+ nodeID: input.node.id,
1460
+ name: [input.name, [Validators.required]],
1461
+ model3D: [input.model3d, [Validators.required]],
1462
+ viewer: input.viewer ? input.viewer : CaptureViewer.MATTERPORT,
1463
+ surface: input.surface ? input.surface : null,
1464
+ });
1465
+ this.captureDate = input.takenAt;
1466
+ }
1467
+ else {
1468
+ this.visitForm = this.fb.group({
1469
+ model3D: [null, [Validators.required]],
1470
+ viewer: CaptureViewer.MATTERPORT,
1471
+ surface: null,
1472
+ name: [null, [Validators.required]],
1473
+ });
1474
+ }
1475
+ }
1476
+ onToggleDetail(index) {
1477
+ this.visitIndexDetails = index === this.visitIndexDetails ? -1 : index;
1478
+ }
1479
+ onAddVisit() {
1480
+ this.initVisitForm();
1481
+ this.menuItems.push({ label: "New" });
1482
+ }
1483
+ onEdit(visit) {
1484
+ this.initVisitForm(visit);
1485
+ this.isEditing = true;
1486
+ this.menuItems.push({ label: visit.name });
1487
+ }
1488
+ async onSubmitVisit() {
1489
+ this.visitSubmitted = true;
1490
+ if (!this.visitForm.valid) {
1491
+ return;
1492
+ }
1493
+ const formValue = this.visitForm.value;
1494
+ const capture = {
1495
+ viewer: formValue.viewer,
1496
+ takenAt: this.captureDate,
1497
+ surface: formValue.surface,
1498
+ spaceID: this.currentSpace.id,
1499
+ };
1500
+ if (this.isEditing) {
1501
+ capture.id = formValue.captureID;
1502
+ await this.captureService.updateCapture(capture);
1503
+ const node = {
1504
+ id: formValue.nodeID,
1505
+ model3D: formValue.model3D,
1506
+ metadata: formValue.name,
1507
+ };
1508
+ await this.nodeService.updateNode(node);
1509
+ }
1510
+ else {
1511
+ let createdNode;
1512
+ await this.captureService.createCapture(capture).then(async (res) => {
1513
+ if (res) {
1514
+ const node = {
1515
+ model3D: formValue.model3D,
1516
+ captureID: res.id,
1517
+ metadata: formValue.name,
1518
+ };
1519
+ createdNode = await this.nodeService.createNode(node);
1520
+ }
1521
+ });
1522
+ // let newMetadata = this.currentSpace.metadata + "," + formValue.model3D;
1523
+ // this.spaceService.update({ id: this.spaceId, metadata: newMetadata});
1524
+ await this.launchImport(formValue.model3D, formValue.surface, createdNode, false);
1525
+ this.modalReference.close();
1526
+ }
1527
+ await this.resetVisits();
1528
+ this.onCancel();
1529
+ }
1530
+ get model3D() {
1531
+ return this.visitForm.get("model3D");
1532
+ }
1533
+ get name() {
1534
+ return this.visitForm.get("name");
1535
+ }
1536
+ onCancel() {
1537
+ this.visitSubmitted = false;
1538
+ this.visitForm = null;
1539
+ this.isEditing = false;
1540
+ this.canCancelImport = false;
1541
+ this.totalImages = 0;
1542
+ this.imagesProcessed = 0;
1543
+ this.setupMenuItems();
1544
+ }
1545
+ async onDelete(visit, index) {
1546
+ await this.visitService.deleteVisit(visit, this.spaceId);
1547
+ this.resetVisits();
1548
+ }
1549
+ getDismissReason(reason) {
1550
+ if (reason === ModalDismissReasons.ESC) {
1551
+ return "by pressing ESC";
1552
+ }
1553
+ if (reason === ModalDismissReasons.BACKDROP_CLICK) {
1554
+ return "by clicking on a backdrop";
1555
+ }
1556
+ return `with: ${reason}`;
1557
+ }
1558
+ async launchImport(model3D, surface, node, imagesOnly = false) {
1559
+ this.modalReference = this.modalService.open(this.content, {
1560
+ ariaLabelledBy: "modal-basic-title",
1561
+ });
1562
+ this.modalReference.result.then((result) => {
1563
+ this.closeResult = `Closed with: ${result}`;
1564
+ console.log(this.closeResult);
1565
+ this.cancelImport();
1566
+ }, (error) => {
1567
+ this.closeResult = `Dismissed ${this.getDismissReason(error)}`;
1568
+ console.log(this.closeResult);
1569
+ this.cancelImport();
1570
+ });
1571
+ const sdkStarted = await this.matterportImportService.initSdkForModel(model3D);
1572
+ await (imagesOnly
1573
+ ? this.matterportImportService.importData(this.spaceId, surface, true, node)
1574
+ : this.matterportImportService.importData(this.spaceId, surface, false, node));
1575
+ return Promise.resolve();
1576
+ }
1577
+ cancelImport() {
1578
+ this.matterportImportService.abandon();
1579
+ this.onCancel();
1580
+ this.resetVisits();
1581
+ }
1582
+ onImport(visit) {
1583
+ this.launchImport(visit.model3d, null, visit.node.id, true);
1584
+ }
1585
+ }
1586
+ VisitsComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: VisitsComponent, deps: [{ token: i1$2.FormBuilder }, { token: i2.MatterportImportService }, { token: i2.CaptureService }, { token: i2.NodeService }, { token: i2.SpaceService }, { token: i1.ActivatedRoute }, { token: i7.NgbModal }, { token: i2.VisitService }], target: i0.ɵɵFactoryTarget.Component });
1587
+ VisitsComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: VisitsComponent, selector: "lib-visits", viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true }], ngImport: i0, template: "<div class=\"container-fluid\" class=\"dashboard-tab\">\r\n <div class=\"m-3\">\r\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\r\n </div>\r\n\r\n <div class=\"row main-form-container\">\r\n <div class=\"col-md-6 form-container\">\r\n <ul class=\"list-group list-group-flush\" *ngIf=\"!visitForm\">\r\n <li class=\"list-group-item list-group-item-action\" *ngFor=\"let visit of visits; index as index\"\r\n (click)=\"onToggleDetail(index)\">\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n {{visit.name ? visit.name : visit.model3d }}\r\n <lib-chevron [conditionShowing]=\"index==visitIndexDetails\"></lib-chevron>\r\n </div>\r\n <div *ngIf=\"index==visitIndexDetails\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate}} : {{ visit.name ?\r\n visit.name : \"Not provided\" | translate}}</li>\r\n <li class=\"list-group-item bg-transparent\">{{'3D Model' | translate}} : {{ visit.model3d }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\">{{'Surface' | translate}}, m<sup>2</sup>: {{\r\n visit.surface ?\r\n visit.surface : 'Not provided' | translate}}</li>\r\n <li class=\"list-group-item bg-transparent\">{{'Viewer' | translate}} : {{ visit.viewer }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\">{{'Date of capture' | translate}} : {{\r\n visit.takenAt | timeDateString | async}}\r\n <li class=\"list-group-item bg-transparent\">{{'Images are imported' | translate}} : {{\r\n visit.imagesImported }}\r\n </li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n *ngIf=\"!visit.imagesImported\" (click)=\"onImport(visit)\">{{'Import images' |\r\n translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n (click)=\"onEdit(visit)\">{{'Edit' | translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\"\r\n (click)=\"onDelete(visit, index)\">{{'Delete' | translate}}</button>\r\n </div>\r\n </li>\r\n </ul>\r\n <div *ngIf=\"visitForm\">\r\n <form (ngSubmit)=\"onSubmitVisit()\" [formGroup]=\"visitForm\">\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Name' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\r\n </div>\r\n </div>\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Reference 3D model' | translate}} *</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" [readonly]=\"isEditing\"\r\n [class.is-invalid]=\"model3D.invalid && visitSubmitted\" required\r\n formControlName=\"model3D\">\r\n <div class=\"invalid-feedback\">\r\n {{'Please provide the 3D model reference' | translate}}\r\n </div>\r\n <div *ngIf=\"isEditing\" class=\"text-danger\">\r\n {{'Cannot change 3D model reference. Please add new model.' | translate}}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Viewer' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"viewer\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Surface' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"surface\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Capture Date' | translate}}</label>\r\n <div class=\"input-group\">\r\n <input class=\"form-control\" placeholder=\"dd/mm/yyyy\" [(ngModel)]=\"captureDate\" ngbDatepicker\r\n #d1=\"ngbDatepicker\" [ngModelOptions]=\"{standalone: true}\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"d1.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n </div>\r\n <button type=\"submit\" class=\"btn btn-outline-primary rounded-pill me-2\">{{'Save' |\r\n translate}}</button>\r\n <button type=\"button\" (click)=\"onCancel()\" class=\"btn btn-outline-primary rounded-pill\">{{'Cancel' |\r\n translate}}</button>\r\n </form>\r\n </div>\r\n <div *ngIf=\"!visitForm\">\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onAddVisit()\">{{'Add 3D visit' |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n <div id=\"viewer-frame\" style=\"width: 100%; aspect-ratio: 2/1;\"></div>\r\n </div>\r\n <ng-template #content let-modal>\r\n <div class=\"modal-header\">\r\n <h4 class=\"modal-title\" id=\"modal-basic-title\">{{'Importing Data' | translate}}</h4>\r\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\r\n <span aria-hidden=\"true\">\u00D7</span>\r\n </button>\r\n </div>\r\n <div class=\"modal-body\">\r\n {{'Please do not leave or refresh this page...' | translate}}\r\n <div *ngIf=\"totalImages != 0\">Imported {{imagesProcessed}} out of {{totalImages}} images.</div>\r\n <div class=\"d-flex justify-content-center\">\r\n <div class=\"spinner-border\" role=\"status\">\r\n <span class=\"visually-hidden\">{{'Loading' | translate}}...</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-outline-dark\" [disabled]=\"!canCancelImport\"\r\n (click)=\"modal.close('Cancel click')\">{{'Cancel import of images' | translate}}</button>\r\n </div>\r\n </ng-template>\r\n</div>", styles: [".col-form-label{width:180px;margin-bottom:1rem}.btn-outline-primary{width:200px;margin-right:1rem}.input-group{width:83%;height:-moz-fit-content;height:fit-content}.main-form-container{width:60vw;display:flex;flex-direction:column}@media screen and (max-width: 765px){.main-form-container{width:90vw}}.main-form-container .form-container{width:100%}\n"], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }, { type: ChevronComponent, selector: "lib-chevron", inputs: ["conditionShowing"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { type: i7.NgbInputDatepicker, selector: "input[ngbDatepicker]", inputs: ["autoClose", "datepickerClass", "dayTemplate", "dayTemplateData", "displayMonths", "firstDayOfWeek", "footerTemplate", "markDisabled", "minDate", "maxDate", "navigation", "outsideDays", "placement", "restoreFocus", "showWeekNumbers", "startDate", "container", "positionTarget", "weekdays", "disabled"], outputs: ["dateSelect", "navigate", "closed"], exportAs: ["ngbDatepicker"] }, { type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], pipes: { "translate": i3.TranslatePipe, "async": i5.AsyncPipe, "timeDateString": i2.TimeDateToLocalStringPipe } });
1588
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: VisitsComponent, decorators: [{
1589
+ type: Component,
1590
+ args: [{ selector: 'lib-visits', template: "<div class=\"container-fluid\" class=\"dashboard-tab\">\r\n <div class=\"m-3\">\r\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\r\n </div>\r\n\r\n <div class=\"row main-form-container\">\r\n <div class=\"col-md-6 form-container\">\r\n <ul class=\"list-group list-group-flush\" *ngIf=\"!visitForm\">\r\n <li class=\"list-group-item list-group-item-action\" *ngFor=\"let visit of visits; index as index\"\r\n (click)=\"onToggleDetail(index)\">\r\n <div class=\"d-flex justify-content-between align-items-center\">\r\n {{visit.name ? visit.name : visit.model3d }}\r\n <lib-chevron [conditionShowing]=\"index==visitIndexDetails\"></lib-chevron>\r\n </div>\r\n <div *ngIf=\"index==visitIndexDetails\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\">{{'Name' | translate}} : {{ visit.name ?\r\n visit.name : \"Not provided\" | translate}}</li>\r\n <li class=\"list-group-item bg-transparent\">{{'3D Model' | translate}} : {{ visit.model3d }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\">{{'Surface' | translate}}, m<sup>2</sup>: {{\r\n visit.surface ?\r\n visit.surface : 'Not provided' | translate}}</li>\r\n <li class=\"list-group-item bg-transparent\">{{'Viewer' | translate}} : {{ visit.viewer }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\">{{'Date of capture' | translate}} : {{\r\n visit.takenAt | timeDateString | async}}\r\n <li class=\"list-group-item bg-transparent\">{{'Images are imported' | translate}} : {{\r\n visit.imagesImported }}\r\n </li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n *ngIf=\"!visit.imagesImported\" (click)=\"onImport(visit)\">{{'Import images' |\r\n translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n (click)=\"onEdit(visit)\">{{'Edit' | translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\"\r\n (click)=\"onDelete(visit, index)\">{{'Delete' | translate}}</button>\r\n </div>\r\n </li>\r\n </ul>\r\n <div *ngIf=\"visitForm\">\r\n <form (ngSubmit)=\"onSubmitVisit()\" [formGroup]=\"visitForm\">\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Name' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"name\">\r\n </div>\r\n </div>\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Reference 3D model' | translate}} *</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" [readonly]=\"isEditing\"\r\n [class.is-invalid]=\"model3D.invalid && visitSubmitted\" required\r\n formControlName=\"model3D\">\r\n <div class=\"invalid-feedback\">\r\n {{'Please provide the 3D model reference' | translate}}\r\n </div>\r\n <div *ngIf=\"isEditing\" class=\"text-danger\">\r\n {{'Cannot change 3D model reference. Please add new model.' | translate}}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Viewer' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"viewer\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Surface' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"surface\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Capture Date' | translate}}</label>\r\n <div class=\"input-group\">\r\n <input class=\"form-control\" placeholder=\"dd/mm/yyyy\" [(ngModel)]=\"captureDate\" ngbDatepicker\r\n #d1=\"ngbDatepicker\" [ngModelOptions]=\"{standalone: true}\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"d1.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n </div>\r\n <button type=\"submit\" class=\"btn btn-outline-primary rounded-pill me-2\">{{'Save' |\r\n translate}}</button>\r\n <button type=\"button\" (click)=\"onCancel()\" class=\"btn btn-outline-primary rounded-pill\">{{'Cancel' |\r\n translate}}</button>\r\n </form>\r\n </div>\r\n <div *ngIf=\"!visitForm\">\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onAddVisit()\">{{'Add 3D visit' |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n <div id=\"viewer-frame\" style=\"width: 100%; aspect-ratio: 2/1;\"></div>\r\n </div>\r\n <ng-template #content let-modal>\r\n <div class=\"modal-header\">\r\n <h4 class=\"modal-title\" id=\"modal-basic-title\">{{'Importing Data' | translate}}</h4>\r\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\r\n <span aria-hidden=\"true\">\u00D7</span>\r\n </button>\r\n </div>\r\n <div class=\"modal-body\">\r\n {{'Please do not leave or refresh this page...' | translate}}\r\n <div *ngIf=\"totalImages != 0\">Imported {{imagesProcessed}} out of {{totalImages}} images.</div>\r\n <div class=\"d-flex justify-content-center\">\r\n <div class=\"spinner-border\" role=\"status\">\r\n <span class=\"visually-hidden\">{{'Loading' | translate}}...</span>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn btn-outline-dark\" [disabled]=\"!canCancelImport\"\r\n (click)=\"modal.close('Cancel click')\">{{'Cancel import of images' | translate}}</button>\r\n </div>\r\n </ng-template>\r\n</div>", styles: [".col-form-label{width:180px;margin-bottom:1rem}.btn-outline-primary{width:200px;margin-right:1rem}.input-group{width:83%;height:-moz-fit-content;height:fit-content}.main-form-container{width:60vw;display:flex;flex-direction:column}@media screen and (max-width: 765px){.main-form-container{width:90vw}}.main-form-container .form-container{width:100%}\n"] }]
1591
+ }], ctorParameters: function () { return [{ type: i1$2.FormBuilder }, { type: i2.MatterportImportService }, { type: i2.CaptureService }, { type: i2.NodeService }, { type: i2.SpaceService }, { type: i1.ActivatedRoute }, { type: i7.NgbModal }, { type: i2.VisitService }]; }, propDecorators: { content: [{
1592
+ type: ViewChild,
1593
+ args: ["content", { static: false }]
1594
+ }] } });
1595
+
1596
+ class CarouselComponent {
1597
+ constructor() {
1598
+ this.currentScan = new EventEmitter();
1599
+ }
1600
+ ngOnInit() {
1601
+ this.currentScan.emit(0);
1602
+ }
1603
+ onSlide(event) {
1604
+ this.dataEvent = JSON.stringify(event);
1605
+ const imageIndex = Number.parseInt(event.current.replace("slideId_", ""), 10);
1606
+ this.currentScan.emit(imageIndex);
1607
+ }
1608
+ }
1609
+ CarouselComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1610
+ CarouselComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: CarouselComponent, selector: "lib-carousel", inputs: { images: "images" }, outputs: { currentScan: "currentScan" }, ngImport: i0, template: "<ngb-carousel [interval]=\"5000\" *ngIf=\"images && images.length > 1\" (slide)=\"onSlide($event)\">\n <ng-template *ngFor=\"let image of images; let i = index\" ngbSlide [id]=\"'slideId_' + i\">\n <div class=\"picsum-img-wrapper\">\n <img [src]=\"image.path\" alt=\"Scan\" class=\"d-block w-100\">\n </div>\n <div class=\"carousel-caption\">\n <h3>{{image.title}}</h3>\n <p>{{image.description}}</p>\n </div>\n </ng-template>\n</ngb-carousel>\n\n<img *ngIf=\"images.length === 1\" [src]=\"images[0].path\" alt=\"Scan\" class=\"d-block w-100\">\n", styles: [""], components: [{ type: i7.NgbCarousel, selector: "ngb-carousel", inputs: ["animation", "activeId", "interval", "wrap", "keyboard", "pauseOnHover", "pauseOnFocus", "showNavigationArrows", "showNavigationIndicators"], outputs: ["slide", "slid"], exportAs: ["ngbCarousel"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.NgbSlide, selector: "ng-template[ngbSlide]", inputs: ["id"], outputs: ["slid"] }] });
1611
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: CarouselComponent, decorators: [{
1612
+ type: Component,
1613
+ args: [{ selector: 'lib-carousel', template: "<ngb-carousel [interval]=\"5000\" *ngIf=\"images && images.length > 1\" (slide)=\"onSlide($event)\">\n <ng-template *ngFor=\"let image of images; let i = index\" ngbSlide [id]=\"'slideId_' + i\">\n <div class=\"picsum-img-wrapper\">\n <img [src]=\"image.path\" alt=\"Scan\" class=\"d-block w-100\">\n </div>\n <div class=\"carousel-caption\">\n <h3>{{image.title}}</h3>\n <p>{{image.description}}</p>\n </div>\n </ng-template>\n</ngb-carousel>\n\n<img *ngIf=\"images.length === 1\" [src]=\"images[0].path\" alt=\"Scan\" class=\"d-block w-100\">\n", styles: [""] }]
1614
+ }], ctorParameters: function () { return []; }, propDecorators: { images: [{
1615
+ type: Input
1616
+ }], currentScan: [{
1617
+ type: Output
1618
+ }] } });
1619
+
1620
+ class SelectionComponent {
1621
+ constructor() {
1622
+ /**Emit a new selection */
1623
+ this.onSelectionChanged = new EventEmitter();
1624
+ this.isMousedown = false;
1625
+ this.selection = { startX: 0, startY: 0, endX: 0, endY: 0 };
1626
+ this.selectionRectangle = { top: 0, left: 0, width: 0, height: 0 };
1627
+ }
1628
+ ngOnInit() {
1629
+ this.selectionableZone = document.querySelector(".selectZone");
1630
+ this.selectionElement = document.querySelector("#selection");
1631
+ this.selectionableZone.addEventListener("mousedown", this.mousedown.bind(this));
1632
+ this.selectionableZone.addEventListener("mousemove", this.mousemove.bind(this));
1633
+ this.selectionableZone.addEventListener("mouseup", this.mouseup.bind(this));
1634
+ }
1635
+ /**
1636
+ * Update the current selection
1637
+ * Will update the selectionRectangle and Selection HTML element
1638
+ * @param shouldEmit if true an event will be emitted through onSelectionChanged (true by default)
1639
+ */
1640
+ updateSelectionElement(shouldEmit = true) {
1641
+ this.selectionRectangle.width = Math.abs(this.selection.endX - this.selection.startX);
1642
+ this.selectionRectangle.height = Math.abs(this.selection.endY - this.selection.startY);
1643
+ this.selectionRectangle.top = Math.min(this.selection.endY, this.selection.startY);
1644
+ this.selectionRectangle.left = Math.min(this.selection.endX, this.selection.startX);
1645
+ this.selectionElement.style.top = `${this.selectionRectangle.top}px`;
1646
+ this.selectionElement.style.left = `${this.selectionRectangle.left}px`;
1647
+ this.selectionElement.style.width = `${this.selectionRectangle.width}px`;
1648
+ this.selectionElement.style.height = `${this.selectionRectangle.height}px`;
1649
+ if (shouldEmit) {
1650
+ this.onSelectionChanged.emit(this.selectionRectangle);
1651
+ }
1652
+ }
1653
+ /**
1654
+ * Trigger when mouse is press down on selectionableZone
1655
+ * @param event
1656
+ */
1657
+ mousedown(event) {
1658
+ if (event.button !== 2) { /**Selection with the right click, so ignore all other event */
1659
+ return;
1660
+ }
1661
+ this.isMousedown = true;
1662
+ this.selection = { startY: event.clientY, startX: event.clientX, endX: event.clientX, endY: event.clientY };
1663
+ this.updateSelectionElement();
1664
+ }
1665
+ /**
1666
+ * Trigger when mouse is release on selectionableZone
1667
+ * @param event
1668
+ */
1669
+ mouseup(event) {
1670
+ this.isMousedown = false;
1671
+ this.selection = { startX: 0, startY: 0, endX: 0, endY: 0 };
1672
+ this.updateSelectionElement(false);
1673
+ }
1674
+ /**
1675
+ * Trigger when mouse is moving on selectionableZone
1676
+ * @param event
1677
+ */
1678
+ mousemove(event) {
1679
+ if (this.isMousedown) {
1680
+ this.selection.endX = event.clientX;
1681
+ this.selection.endY = event.clientY;
1682
+ this.updateSelectionElement();
1683
+ }
1684
+ }
1685
+ }
1686
+ SelectionComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: SelectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1687
+ SelectionComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: SelectionComponent, selector: "lib-selection", outputs: { onSelectionChanged: "onSelectionChanged" }, ngImport: i0, template: "<ng-content></ng-content>\n\n<div id=\"selection\"></div>", styles: ["#selection{will-change:top,left,width,height;top:0px;left:0px;position:fixed;display:block;margin-top:0;margin-left:0;width:0px;height:0px;background:rgba(83,182,187,.3);border-radius:.15em;border:.15em solid rgba(83,182,187,.8);pointer-events:none}\n"] });
1688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: SelectionComponent, decorators: [{
1689
+ type: Component,
1690
+ args: [{ selector: 'lib-selection', template: "<ng-content></ng-content>\n\n<div id=\"selection\"></div>", styles: ["#selection{will-change:top,left,width,height;top:0px;left:0px;position:fixed;display:block;margin-top:0;margin-left:0;width:0px;height:0px;background:rgba(83,182,187,.3);border-radius:.15em;border:.15em solid rgba(83,182,187,.8);pointer-events:none}\n"] }]
1691
+ }], ctorParameters: function () { return []; }, propDecorators: { onSelectionChanged: [{
1692
+ type: Output
1693
+ }] } });
1694
+
1695
+ class AddZoneComponent {
1696
+ constructor(fb, zoneService, layerService, userService, navigationService, planService) {
1697
+ this.fb = fb;
1698
+ this.zoneService = zoneService;
1699
+ this.layerService = layerService;
1700
+ this.userService = userService;
1701
+ this.navigationService = navigationService;
1702
+ this.planService = planService;
1703
+ this.updatedZone = new EventEmitter();
1704
+ this.isMuseumVisit = false;
1705
+ this.parentZones = [];
1706
+ this.carouselIsVisible = false;
1707
+ this.isSubmitted = false;
1708
+ this.chosenScans = [];
1709
+ this.fromPlan = false;
1710
+ this.planCalibrated = false;
1711
+ this.chosenScansOnPlan = [];
1712
+ this.navIDsZoneEdit = [];
1713
+ this.zoneInitialNavCount = 0;
1714
+ /** all scan buttons Element */
1715
+ this.buttonElements = [];
1716
+ /** Scan buttons that are in the current selection */
1717
+ this.buttonsInSelection = [];
1718
+ this.zoneIsMatterportFloor = false;
1719
+ }
1720
+ ngOnInit() {
1721
+ this.parentZones = this.zones.filter((zone) => zone.layer && zone.layer.name === "FLOOR");
1722
+ this.parentZones.unshift(this.defaultZone);
1723
+ this.initZoneForm();
1724
+ this.updateLayers();
1725
+ this.getPlans();
1726
+ }
1727
+ ngOnChanges() {
1728
+ this.initZoneForm();
1729
+ if (this.newZoneData)
1730
+ this.iniPlanForDraw();
1731
+ }
1732
+ async updateLayers() {
1733
+ const mission = this.userService.currentMission(this.spaceID);
1734
+ this.currentOrgId = mission.organisationID;
1735
+ this.layers = await this.layerService.getLayerForOrganisation(this.currentOrgId);
1736
+ }
1737
+ async initZoneForm() {
1738
+ if (this.zoneEdit) {
1739
+ if (this.zoneEdit.metadata) {
1740
+ const metadata = JSON.parse(this.zoneEdit.metadata);
1741
+ this.zoneIsMatterportFloor = metadata.hasOwnProperty("matterportFloorSequence");
1742
+ }
1743
+ const isVirtual = this.zoneService.zoneIsVirtual(this.zoneEdit);
1744
+ if (!isVirtual) {
1745
+ await this.loadNavigations();
1746
+ }
1747
+ this.isMuseumVisit = this.zoneEdit.isMuseumVisitZone
1748
+ ? this.zoneEdit.isMuseumVisitZone
1749
+ : false;
1750
+ this.zoneForm = this.fb.group({
1751
+ id: this.zoneEdit.id,
1752
+ name: [this.zoneEdit.name, [Validators.required]],
1753
+ layerID: this.zoneEdit.layer ? this.zoneEdit.layer.id : null,
1754
+ surface: this.zoneEdit.surface ? this.zoneEdit.surface : 0,
1755
+ parentID: this.zoneEdit.parentID,
1756
+ parent: this.zoneEdit.parent,
1757
+ newLayer: null,
1758
+ isMuseumVisitZone: this.isMuseumVisit,
1759
+ });
1760
+ }
1761
+ else {
1762
+ const mainZone = this.zones.find((z) => !z.parentID);
1763
+ this.zoneForm = this.fb.group({
1764
+ name: [
1765
+ this.newZoneData ? this.newZoneData.name : "",
1766
+ [Validators.required],
1767
+ ],
1768
+ layerID: null,
1769
+ surface: 0,
1770
+ parentID: mainZone.id,
1771
+ newLayer: null,
1772
+ isMuseumVisitZone: this.isMuseumVisit,
1773
+ });
1774
+ }
1775
+ }
1776
+ async getPlans() {
1777
+ this.plans = await this.planService.getSingedPlansForSpace(this.spaceID);
1778
+ }
1779
+ async onSubmitZone() {
1780
+ this.isSubmitted = true;
1781
+ if (!this.zoneForm.valid) {
1782
+ return;
1783
+ }
1784
+ const formValue = this.zoneForm.value;
1785
+ if (formValue.newLayer) {
1786
+ await this.createLayer(formValue.newLayer).then((layer) => {
1787
+ formValue.layerID = layer.id;
1788
+ });
1789
+ }
1790
+ if (!formValue.layerID && !this.isMuseumVisit) {
1791
+ // by defaul assign to layer: "Local" with uuid: d6138c5d-6938-49fa-a5c3-9cebde97594b
1792
+ formValue.layerID = "d6138c5d-6938-49fa-a5c3-9cebde97594b";
1793
+ }
1794
+ const zoneInput = {
1795
+ name: formValue.name,
1796
+ surface: formValue.surface,
1797
+ spaceID: this.spaceID,
1798
+ layerID: formValue.layerID,
1799
+ parentID: formValue.parentID,
1800
+ isMuseumVisitZone: this.isMuseumVisit,
1801
+ sweepIDs: [],
1802
+ };
1803
+ try {
1804
+ this.zoneForm = null;
1805
+ this.isSubmitted = false;
1806
+ if (this.zoneEdit) {
1807
+ const zoneUpdate = zoneInput;
1808
+ zoneUpdate.sweepIDs = this.zoneEdit.sweepIDs;
1809
+ zoneUpdate.id = formValue.id;
1810
+ await this.setSweepsAndNodeForZoneInput(zoneUpdate);
1811
+ console.log(zoneUpdate);
1812
+ await this.zoneService.updateZone(zoneUpdate);
1813
+ this.zoneEdit = null;
1814
+ this.updatedZone.emit(zoneUpdate.id);
1815
+ }
1816
+ else {
1817
+ await this.setSweepsAndNodeForZoneInput(zoneInput);
1818
+ // console.log(zoneInput);
1819
+ await this.zoneService
1820
+ .create(zoneInput)
1821
+ .then(zoneCreated => {
1822
+ this.updatedZone.emit(zoneCreated.id);
1823
+ });
1824
+ }
1825
+ }
1826
+ catch (error) {
1827
+ console.error(error);
1828
+ }
1829
+ }
1830
+ onCancel() {
1831
+ this.isSubmitted = false;
1832
+ this.zoneForm = null;
1833
+ this.chosenScansOnPlan = [];
1834
+ this.chosenScans = [];
1835
+ this.updatedZone.emit(false);
1836
+ }
1837
+ get name() {
1838
+ return this.zoneForm.get("name");
1839
+ }
1840
+ toggleCarousel() {
1841
+ this.carouselIsVisible = true;
1842
+ this.chosenScansOnPlan = [];
1843
+ }
1844
+ togglePlan() {
1845
+ this.fromPlan = true;
1846
+ this.chosenScans = [];
1847
+ }
1848
+ onAddScan() {
1849
+ if (!this.chosenScans.includes(this.currentScanShowing)) {
1850
+ // console.log("adding scan", this.currentScanShowing);
1851
+ this.chosenScans.push(this.currentScanShowing);
1852
+ }
1853
+ else {
1854
+ alert("Scan already added!");
1855
+ }
1856
+ }
1857
+ onRemoveScanImage(scan) {
1858
+ const index = this.chosenScans.indexOf(scan);
1859
+ this.chosenScans.splice(index, 1);
1860
+ }
1861
+ onRemoveScanPlan(scan) {
1862
+ const index = this.chosenScansOnPlan.indexOf(scan);
1863
+ this.chosenScansOnPlan.splice(index, 1);
1864
+ }
1865
+ async setSweepsAndNodeForZoneInput(zoneInput) {
1866
+ if (this.chosenScans.length > 0 &&
1867
+ this.chosenScans.length !== this.zoneInitialNavCount &&
1868
+ !this.fromPlan) {
1869
+ // if we chose scans from the carousel
1870
+ const sweepIDs = [];
1871
+ this.chosenScans.map(async (scan) => {
1872
+ const imageUuid = this.images360[scan].filename.split("_")[0];
1873
+ const sweepID = this.navigationIDs.find((nav) => {
1874
+ return nav.includes(imageUuid);
1875
+ });
1876
+ if (sweepID) {
1877
+ sweepIDs.push(sweepID);
1878
+ }
1879
+ });
1880
+ zoneInput.sweepIDs = sweepIDs;
1881
+ }
1882
+ else {
1883
+ // we are not coming from gallery choice
1884
+ if (this.chosenScansOnPlan.length > 0) {
1885
+ zoneInput.sweepIDs = this.chosenScansOnPlan;
1886
+ }
1887
+ }
1888
+ if (zoneInput.sweepIDs && zoneInput.sweepIDs.length > 0) {
1889
+ const navForSweep = await this.navigationService.getNavigationsForMatterportIDForSpace(zoneInput.sweepIDs[0], this.spaceID);
1890
+ zoneInput.nodeIDs = [navForSweep[0].nodeID];
1891
+ }
1892
+ }
1893
+ async createLayer(name) {
1894
+ return this.layerService.createLayerForOrganisation(name, this.currentOrgId);
1895
+ }
1896
+ async loadNavigations() {
1897
+ if (this.zoneEdit.sweepIDs.length > 0) {
1898
+ this.zoneInitialNavCount = this.zoneEdit.sweepIDs.length;
1899
+ const navList = [];
1900
+ const scansZoneEdit = [];
1901
+ await Promise.all(this.zoneEdit.sweepIDs.map(async (sweepID) => {
1902
+ scansZoneEdit.push(sweepID);
1903
+ const index = this.images360.findIndex((im) => {
1904
+ return im.filename.includes(sweepID.slice(0, 8));
1905
+ });
1906
+ if (index !== -1) {
1907
+ navList.push(index);
1908
+ }
1909
+ }));
1910
+ this.chosenScans = navList;
1911
+ this.navIDsZoneEdit = scansZoneEdit;
1912
+ this.chosenScansOnPlan = scansZoneEdit;
1913
+ return Promise.resolve();
1914
+ }
1915
+ return Promise.resolve();
1916
+ }
1917
+ async onPlanClick(plan = this.chosenPlan) {
1918
+ this.chosenScansOnPlan = [];
1919
+ this.chosenPlan = plan;
1920
+ this.clearDivPlan();
1921
+ this.addScanPoints();
1922
+ }
1923
+ async addScanPoints() {
1924
+ this.divPlan = document.querySelector("#planDiv");
1925
+ const zoneOfPlan = await this.zoneService.getZone(this.chosenPlan.zoneID);
1926
+ const navigations = await this.navigationService.getNavigationsForZone(zoneOfPlan);
1927
+ if (this.newZoneData)
1928
+ await this.setScanByDraw(navigations);
1929
+ this.buttonElements = await showScanPointsOnPlanInDiv(this.chosenPlan, this.divPlan, navigations);
1930
+ for (const element of this.buttonElements) {
1931
+ if (this.navIDsZoneEdit.length > 0 &&
1932
+ this.navIDsZoneEdit.includes(element.id) // button id is the navigation(not graph!) ids
1933
+ ) {
1934
+ this.styleButton(element, true);
1935
+ this.chosenScansOnPlan.push(element.id);
1936
+ }
1937
+ else {
1938
+ this.styleButton(element);
1939
+ }
1940
+ element.addEventListener("click", (event) => this.onButtonScanClicked(event.target));
1941
+ }
1942
+ this.panZoom = panzoom(this.divPlan, {
1943
+ bounds: true,
1944
+ boundsPadding: 0,
1945
+ maxZoom: 3.5,
1946
+ beforeMouseDown(e) {
1947
+ return (e.button === 2); /** Ignore event for right click (use for selecting) */
1948
+ },
1949
+ });
1950
+ }
1951
+ /**
1952
+ * When a button representing a scan is clicked
1953
+ * @param button
1954
+ */
1955
+ onButtonScanClicked(button) {
1956
+ button.style.backgroundImage = this.buttonIsChosen(button)
1957
+ ? `url("https://api.iconify.design/mdi:close-circle-outline.svg?color=red&height=17&width=17")`
1958
+ : `url("https://api.iconify.design/mdi:adjust.svg?color=green&height=17&width=17")`;
1959
+ if (this.buttonIsChosen(button)) {
1960
+ if (!this.chosenScansOnPlan.includes(button.id)) {
1961
+ this.chosenScansOnPlan.push(button.id);
1962
+ }
1963
+ }
1964
+ else if (this.chosenScansOnPlan.includes(button.id)) {
1965
+ const index = this.chosenScansOnPlan.indexOf(button.id);
1966
+ this.chosenScansOnPlan.splice(index, 1);
1967
+ }
1968
+ }
1969
+ styleButton(button, isAdded = false) {
1970
+ button.style.backgroundImage = isAdded
1971
+ ? `url("https://api.iconify.design/mdi:adjust.svg?color=green&height=17&width=17")`
1972
+ : `url("https://api.iconify.design/mdi:close-circle-outline.svg?color=red&height=17&width=17")`;
1973
+ button.style.backgroundColor = "transparent";
1974
+ button.style.position = "absolute";
1975
+ button.style.border = "none";
1976
+ button.style.width = "17px";
1977
+ button.style.height = "17px";
1978
+ button.disabled = false;
1979
+ button.type = "button";
1980
+ }
1981
+ buttonIsChosen(button) {
1982
+ return button.style.backgroundImage.includes("adjust");
1983
+ }
1984
+ clearDivPlan() {
1985
+ if (this.divPlan)
1986
+ this.divPlan.innerHTML = "";
1987
+ }
1988
+ iniPlanForDraw() {
1989
+ this.togglePlan();
1990
+ setTimeout(() => {
1991
+ this.onPlanClick();
1992
+ }, 500);
1993
+ }
1994
+ setScanByDraw(navigations) {
1995
+ return new Promise(async (resolve) => {
1996
+ const rect = {
1997
+ width: this.newZoneData.width,
1998
+ height: this.newZoneData.height,
1999
+ };
2000
+ const img = await getMetaForImage(this.chosenPlan.filepath);
2001
+ const { coeffX, coeffY } = getCoefficientsForImage(img, rect);
2002
+ const draw = this.newZoneData.elements;
2003
+ draw.map(async (element) => {
2004
+ switch (element.nodeName) {
2005
+ case "rect":
2006
+ await this.setScanInRect(coeffX, coeffY, element, navigations);
2007
+ break;
2008
+ case "ellipse":
2009
+ await this.setScanInCircle(coeffX, coeffY, element, navigations);
2010
+ break;
2011
+ case "path":
2012
+ await this.setScanInPolygon(coeffX, coeffY, element, navigations);
2013
+ break;
2014
+ default:
2015
+ break;
2016
+ }
2017
+ });
2018
+ resolve();
2019
+ });
2020
+ }
2021
+ async setScanInRect(coeffX, coeffY, element, navigations) {
2022
+ const calibrage = JSON.parse(this.chosenPlan.calibration);
2023
+ navigations.map((navigation) => {
2024
+ const position = JSON.parse(navigation.position);
2025
+ let navX = (calibrage.offsetX + position.x * calibrage.x) / coeffX;
2026
+ let navY = (calibrage.offsetY + position.z * calibrage.y) / coeffY;
2027
+ if (element.angle) {
2028
+ const rotatePos = this.rotate({ x: navX, y: navY }, { x: element.cx, y: element.cy }, element.angle);
2029
+ navX = rotatePos.x;
2030
+ navY = rotatePos.y;
2031
+ }
2032
+ /**
2033
+ * TODO calculer la rotation du rectangle
2034
+ */
2035
+ if (navX > element.x &&
2036
+ navX < element.x + element.width &&
2037
+ navY > element.y &&
2038
+ navY < element.y + element.height &&
2039
+ !this.navIDsZoneEdit.includes(navigation.id)) {
2040
+ this.navIDsZoneEdit.push(navigation.id);
2041
+ }
2042
+ });
2043
+ } // EO
2044
+ async setScanInCircle(coeffX, coeffY, element, navigations) {
2045
+ const calibrage = JSON.parse(this.chosenPlan.calibration);
2046
+ navigations.map(navigation => {
2047
+ const position = JSON.parse(navigation.position);
2048
+ let navX = (calibrage.offsetX + position.x * calibrage.x) / coeffX;
2049
+ let navY = (calibrage.offsetY + position.z * calibrage.y) / coeffY;
2050
+ if (element.angle) {
2051
+ const rotatePos = this.rotate({ x: navX, y: navY }, { x: element.cx, y: element.cy }, element.angle);
2052
+ navX = rotatePos.x;
2053
+ navY = rotatePos.y;
2054
+ }
2055
+ const valueX = Math.pow(navX - element.cx, 2) / Math.pow(element.rx, 2);
2056
+ const valueY = Math.pow(navY - element.cy, 2) / Math.pow(element.ry, 2);
2057
+ const result = valueX + valueY;
2058
+ if (result <= 1 && !this.navIDsZoneEdit.includes(navigation.id)) {
2059
+ this.navIDsZoneEdit.push(navigation.id);
2060
+ }
2061
+ });
2062
+ } // EO
2063
+ async setScanInPolygon(coeffX, coeffY, element, navigations) {
2064
+ const calibrage = JSON.parse(this.chosenPlan.calibration);
2065
+ navigations.map(async (navigation) => {
2066
+ const position = JSON.parse(navigation.position);
2067
+ const p = {
2068
+ x: (calibrage.offsetX + position.x * calibrage.x) / coeffX,
2069
+ y: (calibrage.offsetY + position.z * calibrage.y) / coeffY,
2070
+ };
2071
+ // if (element.angle) {
2072
+ // p = this.rotate(
2073
+ // p,
2074
+ // { x: element.cx, y: element.cy },
2075
+ // element.angle,
2076
+ // );
2077
+ // }
2078
+ if ((await this.isInPolygon(p, element.segList)) &&
2079
+ !this.navIDsZoneEdit.includes(navigation.id)) {
2080
+ this.navIDsZoneEdit.push(navigation.id);
2081
+ }
2082
+ });
2083
+ }
2084
+ async isInPolygon(p, polygon) {
2085
+ return new Promise((resolve) => {
2086
+ let isInside = false;
2087
+ let minX = polygon[0].x;
2088
+ let maxX = polygon[0].x;
2089
+ let minY = polygon[0].y;
2090
+ let maxY = polygon[0].y;
2091
+ for (let n = 1; n < polygon.length; n++) {
2092
+ const q = polygon[n];
2093
+ minX = Math.min(q.x, minX);
2094
+ maxX = Math.max(q.x, maxX);
2095
+ minY = Math.min(q.y, minY);
2096
+ maxY = Math.max(q.y, maxY);
2097
+ }
2098
+ if (p.x < minX || p.x > maxX || p.y < minY || p.y > maxY) {
2099
+ resolve(false);
2100
+ }
2101
+ for (let index = 0, index_ = polygon.length - 1; index < polygon.length; index_ = index++) {
2102
+ if (polygon[index].y > p.y !== polygon[index_].y > p.y &&
2103
+ p.x <
2104
+ ((polygon[index_].x - polygon[index].x) *
2105
+ (p.y - polygon[index].y)) /
2106
+ (polygon[index_].y - polygon[index].y) +
2107
+ polygon[index].x) {
2108
+ isInside = !isInside;
2109
+ }
2110
+ }
2111
+ resolve(isInside);
2112
+ });
2113
+ }
2114
+ rotate(point, center, angle) {
2115
+ let xM;
2116
+ let yM;
2117
+ let x;
2118
+ let y;
2119
+ angle *= Math.PI / 180;
2120
+ xM = point.x - center.x;
2121
+ yM = point.y - center.y;
2122
+ x = xM * Math.cos(angle) + yM * Math.sin(angle) + center.x;
2123
+ y = -xM * Math.sin(angle) + yM * Math.cos(angle) + center.y;
2124
+ return { x: Math.round(x), y: Math.round(y) };
2125
+ }
2126
+ /**
2127
+ * Check if an HtmlElement is inside a selection
2128
+ * @param button HTMLElement
2129
+ * @param selection
2130
+ * @returns true if the element is inside the selection, false otherwise
2131
+ */
2132
+ buttonIsInSelection(button, selection) {
2133
+ const bounds = button.getBoundingClientRect();
2134
+ return (bounds.bottom > selection.top &&
2135
+ bounds.top < selection.top + selection.height &&
2136
+ bounds.right > selection.left &&
2137
+ bounds.left < selection.left + selection.width);
2138
+ }
2139
+ /**
2140
+ * Trigger each by the selectionComponent when selection change
2141
+ * Update the current selected scan buttons
2142
+ * @param selection new selection
2143
+ */
2144
+ onSelectionChanged(selection) {
2145
+ const unselectedButtons = [];
2146
+ const newSelectedButtons = [];
2147
+ /** For each button, decide whether will be add to selected list or unselected list */
2148
+ for (const button of this.buttonElements) {
2149
+ const isSelected = this.buttonIsInSelection(button, selection);
2150
+ if (this.buttonsInSelection.includes(button)) {
2151
+ /** If the button is already in the selected list, we check if it should be unselect */
2152
+ if (!isSelected) {
2153
+ unselectedButtons.push(button);
2154
+ }
2155
+ }
2156
+ else if (isSelected) {
2157
+ newSelectedButtons.push(button);
2158
+ }
2159
+ }
2160
+ /** Visually Update unselected list */
2161
+ for (const button of unselectedButtons) {
2162
+ this.setVisuallySelected(button, false);
2163
+ }
2164
+ /** Visually Update selected list */
2165
+ for (const button of newSelectedButtons) {
2166
+ this.setVisuallySelected(button, true);
2167
+ }
2168
+ /** Remove unselected list */
2169
+ this.buttonsInSelection = this.buttonsInSelection.filter((button) => {
2170
+ return !unselectedButtons.includes(button);
2171
+ });
2172
+ /** Add new selected buttons */
2173
+ this.buttonsInSelection = [
2174
+ ...this.buttonsInSelection,
2175
+ ...newSelectedButtons,
2176
+ ];
2177
+ }
2178
+ /**
2179
+ * Visually change the state of a scan button, a selected button is blue
2180
+ * @param button
2181
+ * @param selected if true the button will be blue, otherwise it will red or green
2182
+ */
2183
+ setVisuallySelected(button, selected) {
2184
+ if (selected) {
2185
+ /** Color in blue */
2186
+ button.style.backgroundImage = button.style.backgroundImage
2187
+ .replace("red", "blue")
2188
+ .replace("green", "blue");
2189
+ }
2190
+ else {
2191
+ /** Get back in red or green */
2192
+ const color = this.buttonIsChosen(button) ? "green" : "red";
2193
+ button.style.backgroundImage = button.style.backgroundImage.replace("blue", color);
2194
+ }
2195
+ }
2196
+ /**
2197
+ * Trigger when the user click to add the selected scans
2198
+ */
2199
+ onSelectionAdd() {
2200
+ for (const button of this.buttonsInSelection) {
2201
+ if (!this.chosenScansOnPlan.includes(button.id)) {
2202
+ this.onButtonScanClicked(button);
2203
+ }
2204
+ }
2205
+ this.onSelectionChanged({ top: 0, left: 0, width: 0, height: 0 });
2206
+ }
2207
+ /**
2208
+ * Trigger when the user click to remove the selected scans
2209
+ */
2210
+ onSelectionRemove() {
2211
+ for (const button of this.buttonsInSelection) {
2212
+ if (this.chosenScansOnPlan.includes(button.id)) {
2213
+ this.onButtonScanClicked(button);
2214
+ }
2215
+ }
2216
+ this.onSelectionChanged({ top: 0, left: 0, width: 0, height: 0 });
2217
+ }
2218
+ /**
2219
+ * Trigger when the user click to select all scans
2220
+ */
2221
+ onSelectAll() {
2222
+ this.buttonsInSelection = [...this.buttonElements];
2223
+ for (const button of this.buttonsInSelection) {
2224
+ this.setVisuallySelected(button, true);
2225
+ }
2226
+ }
2227
+ /**
2228
+ * Trigger when the user change the size of the scan points
2229
+ * @param event
2230
+ */
2231
+ onChangeScanSize(event) {
2232
+ const factor = event.target.value;
2233
+ for (const button of this.buttonElements) {
2234
+ button.style.transform = `scale(${factor})`;
2235
+ }
2236
+ }
2237
+ /**
2238
+ * Adds all sweeps of the space
2239
+ */
2240
+ onAddAll() {
2241
+ const defaultZone = this.zones.find((zone) => zone.layer && zone.layer.name === "BUILDING" && zone.sweepIDs);
2242
+ if (defaultZone) {
2243
+ this.chosenScansOnPlan = defaultZone.sweepIDs;
2244
+ this.zoneForm.get("parentID").setValue(defaultZone.id);
2245
+ }
2246
+ }
2247
+ }
2248
+ AddZoneComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: AddZoneComponent, deps: [{ token: i1$2.FormBuilder }, { token: i2.ZoneService }, { token: i2.LayerService }, { token: i2.BaseUserService }, { token: i2.NavigationService }, { token: i2.PlanService }], target: i0.ɵɵFactoryTarget.Component });
2249
+ AddZoneComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: AddZoneComponent, selector: "lib-add-zone", inputs: { zoneEdit: "zoneEdit", spaceID: "spaceID", images360: "images360", navigationIDs: "navigationIDs", zones: "zones", newZoneData: "newZoneData", chosenPlan: "chosenPlan", isMuseumVisit: "isMuseumVisit", defaultZone: "defaultZone" }, outputs: { updatedZone: "updatedZone" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"row\" *ngIf=\"zoneForm\">\r\n <div class=\"col-7\">\r\n <form (ngSubmit)=\"onSubmitZone()\" [formGroup]=\"zoneForm\">\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Name' | translate}} *</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" [class.is-invalid]=\"name.invalid && isSubmitted\" required\r\n formControlName=\"name\">\r\n <div class=\"invalid-feedback\">\r\n {{'A name is required' | translate}}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" *ngIf=\"!isMuseumVisit && !zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Type of zone' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <select class=\"form-control\" formControlName=\"layerID\">\r\n <option value=\"null\" selected disabled hidden> {{ zoneEdit && zoneEdit.layer ?\r\n zoneEdit.layer.name : 'Choose type'}}</option>\r\n <option *ngFor=\"let layer of layers\" [value]=\"layer.id\">\r\n {{ layer.name }}\r\n </option>\r\n </select>\r\n <p class=\"mt-1 new-type-text\">{{'or add new type of zone' | translate }}</p>\r\n <input type=\"text\" class=\"form-control new-type-input\" formControlName=\"newLayer\">\r\n </div>\r\n </div>\r\n <!--If Editing Zone possibility to assign it to museum itinerary-->\r\n <div class=\"mb-3 row\" *ngIf=\"zoneEdit && !zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{ 'Museum Itinerary' | translate }}</label>\r\n <div class=\"col-sm-10\">\r\n <input class=\"checkbox-apply mt-3\" type=\"checkbox\" [(ngModel)]=\"isMuseumVisit\"\r\n [ngModelOptions]=\"{standalone: true}\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Surface' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"surface\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" *ngIf=\"!zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Parent zone' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <select class=\"form-control\" formControlName=\"parentID\">\r\n <option *ngFor=\"let zone of parentZones\" [ngValue]=\"zone.id\">\r\n {{ zone.name }}\r\n </option>\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" style=\"max-height: 200px; overflow: auto;\" *ngIf=\"!zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Scan Points' | translate}}</label>\r\n <!-- <p class=\"col-sm-2\" *ngIf=\"zoneEdit\">{{ zoneEdit.sweepIDs.length }} scans</p> -->\r\n <p class=\"col\" *ngIf=\"chosenScansOnPlan.length === 0 && chosenScans.length === 0\">\r\n {{'No scan points chosen' | translate}}</p>\r\n <div class=\"col-sm-5\" *ngIf=\"chosenScansOnPlan && chosenScansOnPlan.length > 0\">\r\n <p>{{ chosenScansOnPlan.length }} {{'scan points chosen' | translate}}</p>\r\n <div *ngFor=\"let scan of chosenScansOnPlan\">\r\n <div class=\"row\">\r\n <p class=\"me-2\">{{ scan | slice:0:8 }}</p>\r\n <div (click)=\"onRemoveScanPlan(scan)\" [style.cursor]=\"'pointer'\">\r\n <span class=\"iconify\" data-icon=\"mdi:trash-can-outline\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"col-sm-5\" *ngIf=\"carouselIsVisible && chosenScans && chosenScans.length > 0\">\r\n <p>{{ chosenScans.length }} {{'scan points chosen' | translate}}</p>\r\n <div *ngFor=\"let scan of chosenScans\">\r\n <div class=\"row\">\r\n <p class=\"me-2\">{{ images360[scan].filename }}</p>\r\n <div (click)=\"onRemoveScanImage(scan)\" [style.cursor]=\"'pointer'\">\r\n <span class=\"iconify\" data-icon=\"mdi:trash-can-outline\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" *ngIf=\"!zoneIsMatterportFloor\">\r\n <div class=\"col\">\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" [disabled]=\"!plans\"\r\n (click)=\"togglePlan()\">{{'Choose scan points on plan' |\r\n translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"toggleCarousel()\"\r\n [disabled]=\"fromPlan || !images360 ||images360.length === 0\">\r\n {{'Choose scan points from gallery' | translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onAddAll()\"\r\n [disabled]=\"fromPlan || !images360 ||images360.length === 0\">\r\n {{'Add all scans of this space' | translate}}</button>\r\n </div>\r\n </div>\r\n\r\n <!-- <div class=\"mb-3 row\">\r\n <label class=\"col-sm-3 col-form-label\">{{'No visible scan points' | translate}}</label>\r\n <div class=\"col-sm-3\">\r\n <input class=\"checkbox-apply mt-3\" type=\"checkbox\" [(ngModel)]=\"noScanPoints\"\r\n [ngModelOptions]=\"{standalone: true}\">\r\n </div>\r\n </div> -->\r\n\r\n <div *ngIf=\"carouselIsVisible\" class=\"mb-2\">\r\n <lib-carousel *ngIf=\"images360 && images360.length > 0\" [images]=\"images360\"\r\n (currentScan)=\"currentScanShowing=$event\"></lib-carousel>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2 mt-3\"\r\n (click)=\"onAddScan()\">{{'Add to zone' | translate}}</button>\r\n </div>\r\n <div ngbDropdown class=\"row d-inline-block me-3\" *ngIf=\"fromPlan\">\r\n <div class=\"col\">\r\n <button type=\"button\" class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\"\r\n ngbDropdownToggle>{{chosenPlan ?\r\n chosenPlan.name : ('Choose Plan' | translate) }}</button>\r\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\r\n <button type=\"button\" ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\"\r\n [disabled]=\"!plan.calibration\">{{ plan.name }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"row mt-1\" *ngIf=\"fromPlan\">\r\n <lib-selection class=\"col-md-8\" (onSelectionChanged)=\"onSelectionChanged($event)\">\r\n <div class=\"selectZone\" oncontextmenu=\"return false;\">\r\n <div class=\"row\">\r\n <div style=\"overflow: hidden;width:100%;\">\r\n <div style=\"height: 400px; width: 100%;\" id=\"planDiv\">\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </lib-selection>\r\n\r\n <div class=\"col-md-4 scanTool\" *ngIf=\"buttonElements.length > 0\">\r\n <div class=\"row mb-5 d-inline-block\">\r\n <h2>Selection tool</h2>\r\n <div class=\"protip\">Hold right click and move your cursor to select multiple scans.</div>\r\n </div>\r\n\r\n <div class=\"row mb-4\">\r\n <div class=\"col-3\" style=\"padding:0px\">\r\n <label class=\"tool-label\" for=\"scanSize\">Scan size</label>\r\n </div>\r\n <div class=\"col-9\">\r\n <input type=\"range\" class=\"form-range\" min=\"0.001\" max=\"5\" step=\"0.01\" value=\"1\"\r\n id=\"scanSize\" (input)=\"onChangeScanSize($event)\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"row mb-4\">\r\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSelectAll()\">Select all scans</button>\r\n </div>\r\n\r\n <div *ngIf=\"buttonsInSelection.length > 0\" class=\"row editSelected\">\r\n <span class=\"tool-label\">Edit selected scans : </span>\r\n <button type=\"button\" class=\"btn btn-primary ms-3 me-2\" (click)=\"onSelectionAdd()\">Add</button>\r\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSelectionRemove()\">Remove</button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mt-3\">\r\n <button type=\"submit\" class=\"btn btn-outline-primary rounded-pill me-2\">{{'Save' | translate}}</button>\r\n <button type=\"button\" (click)=\"onCancel()\" class=\"btn btn-outline-primary rounded-pill me-2\">\r\n {{'Cancel' | translate}}</button>\r\n </div>\r\n </form>\r\n </div>\r\n</div>", styles: [".form-range{width:100%}.scanTool{padding-left:50px;padding-right:50px}h2{color:var(--smarterplan-primary)}.protip{color:gray}.tool-label{font-size:1.3rem}.selectZone{background-color:gray;border-radius:1rem;overflow:hidden}.col-form-label{margin-bottom:1rem}.btn-outline-primary{min-width:200px;margin-bottom:1rem;margin-right:1rem}.new-type-input{margin-bottom:1rem}.new-type-text{margin-bottom:.5rem}\n"], components: [{ type: CarouselComponent, selector: "lib-carousel", inputs: ["images"], outputs: ["currentScan"] }, { type: SelectionComponent, selector: "lib-selection", outputs: ["onSelectionChanged"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { type: i7.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { type: i7.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { type: i7.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { type: i7.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["disabled"] }], pipes: { "translate": i3.TranslatePipe, "slice": i5.SlicePipe } });
2250
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: AddZoneComponent, decorators: [{
2251
+ type: Component,
2252
+ args: [{ selector: 'lib-add-zone', template: "<div class=\"row\" *ngIf=\"zoneForm\">\r\n <div class=\"col-7\">\r\n <form (ngSubmit)=\"onSubmitZone()\" [formGroup]=\"zoneForm\">\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Name' | translate}} *</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" [class.is-invalid]=\"name.invalid && isSubmitted\" required\r\n formControlName=\"name\">\r\n <div class=\"invalid-feedback\">\r\n {{'A name is required' | translate}}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" *ngIf=\"!isMuseumVisit && !zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Type of zone' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <select class=\"form-control\" formControlName=\"layerID\">\r\n <option value=\"null\" selected disabled hidden> {{ zoneEdit && zoneEdit.layer ?\r\n zoneEdit.layer.name : 'Choose type'}}</option>\r\n <option *ngFor=\"let layer of layers\" [value]=\"layer.id\">\r\n {{ layer.name }}\r\n </option>\r\n </select>\r\n <p class=\"mt-1 new-type-text\">{{'or add new type of zone' | translate }}</p>\r\n <input type=\"text\" class=\"form-control new-type-input\" formControlName=\"newLayer\">\r\n </div>\r\n </div>\r\n <!--If Editing Zone possibility to assign it to museum itinerary-->\r\n <div class=\"mb-3 row\" *ngIf=\"zoneEdit && !zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{ 'Museum Itinerary' | translate }}</label>\r\n <div class=\"col-sm-10\">\r\n <input class=\"checkbox-apply mt-3\" type=\"checkbox\" [(ngModel)]=\"isMuseumVisit\"\r\n [ngModelOptions]=\"{standalone: true}\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Surface' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <input type=\"text\" class=\"form-control\" formControlName=\"surface\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" *ngIf=\"!zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Parent zone' | translate}}</label>\r\n <div class=\"col-sm-10\">\r\n <select class=\"form-control\" formControlName=\"parentID\">\r\n <option *ngFor=\"let zone of parentZones\" [ngValue]=\"zone.id\">\r\n {{ zone.name }}\r\n </option>\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" style=\"max-height: 200px; overflow: auto;\" *ngIf=\"!zoneIsMatterportFloor\">\r\n <label class=\"col-sm-2 col-form-label\">{{'Scan Points' | translate}}</label>\r\n <!-- <p class=\"col-sm-2\" *ngIf=\"zoneEdit\">{{ zoneEdit.sweepIDs.length }} scans</p> -->\r\n <p class=\"col\" *ngIf=\"chosenScansOnPlan.length === 0 && chosenScans.length === 0\">\r\n {{'No scan points chosen' | translate}}</p>\r\n <div class=\"col-sm-5\" *ngIf=\"chosenScansOnPlan && chosenScansOnPlan.length > 0\">\r\n <p>{{ chosenScansOnPlan.length }} {{'scan points chosen' | translate}}</p>\r\n <div *ngFor=\"let scan of chosenScansOnPlan\">\r\n <div class=\"row\">\r\n <p class=\"me-2\">{{ scan | slice:0:8 }}</p>\r\n <div (click)=\"onRemoveScanPlan(scan)\" [style.cursor]=\"'pointer'\">\r\n <span class=\"iconify\" data-icon=\"mdi:trash-can-outline\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"col-sm-5\" *ngIf=\"carouselIsVisible && chosenScans && chosenScans.length > 0\">\r\n <p>{{ chosenScans.length }} {{'scan points chosen' | translate}}</p>\r\n <div *ngFor=\"let scan of chosenScans\">\r\n <div class=\"row\">\r\n <p class=\"me-2\">{{ images360[scan].filename }}</p>\r\n <div (click)=\"onRemoveScanImage(scan)\" [style.cursor]=\"'pointer'\">\r\n <span class=\"iconify\" data-icon=\"mdi:trash-can-outline\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-3 row\" *ngIf=\"!zoneIsMatterportFloor\">\r\n <div class=\"col\">\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" [disabled]=\"!plans\"\r\n (click)=\"togglePlan()\">{{'Choose scan points on plan' |\r\n translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"toggleCarousel()\"\r\n [disabled]=\"fromPlan || !images360 ||images360.length === 0\">\r\n {{'Choose scan points from gallery' | translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onAddAll()\"\r\n [disabled]=\"fromPlan || !images360 ||images360.length === 0\">\r\n {{'Add all scans of this space' | translate}}</button>\r\n </div>\r\n </div>\r\n\r\n <!-- <div class=\"mb-3 row\">\r\n <label class=\"col-sm-3 col-form-label\">{{'No visible scan points' | translate}}</label>\r\n <div class=\"col-sm-3\">\r\n <input class=\"checkbox-apply mt-3\" type=\"checkbox\" [(ngModel)]=\"noScanPoints\"\r\n [ngModelOptions]=\"{standalone: true}\">\r\n </div>\r\n </div> -->\r\n\r\n <div *ngIf=\"carouselIsVisible\" class=\"mb-2\">\r\n <lib-carousel *ngIf=\"images360 && images360.length > 0\" [images]=\"images360\"\r\n (currentScan)=\"currentScanShowing=$event\"></lib-carousel>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2 mt-3\"\r\n (click)=\"onAddScan()\">{{'Add to zone' | translate}}</button>\r\n </div>\r\n <div ngbDropdown class=\"row d-inline-block me-3\" *ngIf=\"fromPlan\">\r\n <div class=\"col\">\r\n <button type=\"button\" class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\"\r\n ngbDropdownToggle>{{chosenPlan ?\r\n chosenPlan.name : ('Choose Plan' | translate) }}</button>\r\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\r\n <button type=\"button\" ngbDropdownItem *ngFor=\"let plan of plans\" (click)=\"onPlanClick(plan)\"\r\n [disabled]=\"!plan.calibration\">{{ plan.name }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"row mt-1\" *ngIf=\"fromPlan\">\r\n <lib-selection class=\"col-md-8\" (onSelectionChanged)=\"onSelectionChanged($event)\">\r\n <div class=\"selectZone\" oncontextmenu=\"return false;\">\r\n <div class=\"row\">\r\n <div style=\"overflow: hidden;width:100%;\">\r\n <div style=\"height: 400px; width: 100%;\" id=\"planDiv\">\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </lib-selection>\r\n\r\n <div class=\"col-md-4 scanTool\" *ngIf=\"buttonElements.length > 0\">\r\n <div class=\"row mb-5 d-inline-block\">\r\n <h2>Selection tool</h2>\r\n <div class=\"protip\">Hold right click and move your cursor to select multiple scans.</div>\r\n </div>\r\n\r\n <div class=\"row mb-4\">\r\n <div class=\"col-3\" style=\"padding:0px\">\r\n <label class=\"tool-label\" for=\"scanSize\">Scan size</label>\r\n </div>\r\n <div class=\"col-9\">\r\n <input type=\"range\" class=\"form-range\" min=\"0.001\" max=\"5\" step=\"0.01\" value=\"1\"\r\n id=\"scanSize\" (input)=\"onChangeScanSize($event)\">\r\n </div>\r\n </div>\r\n\r\n <div class=\"row mb-4\">\r\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSelectAll()\">Select all scans</button>\r\n </div>\r\n\r\n <div *ngIf=\"buttonsInSelection.length > 0\" class=\"row editSelected\">\r\n <span class=\"tool-label\">Edit selected scans : </span>\r\n <button type=\"button\" class=\"btn btn-primary ms-3 me-2\" (click)=\"onSelectionAdd()\">Add</button>\r\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSelectionRemove()\">Remove</button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"mt-3\">\r\n <button type=\"submit\" class=\"btn btn-outline-primary rounded-pill me-2\">{{'Save' | translate}}</button>\r\n <button type=\"button\" (click)=\"onCancel()\" class=\"btn btn-outline-primary rounded-pill me-2\">\r\n {{'Cancel' | translate}}</button>\r\n </div>\r\n </form>\r\n </div>\r\n</div>", styles: [".form-range{width:100%}.scanTool{padding-left:50px;padding-right:50px}h2{color:var(--smarterplan-primary)}.protip{color:gray}.tool-label{font-size:1.3rem}.selectZone{background-color:gray;border-radius:1rem;overflow:hidden}.col-form-label{margin-bottom:1rem}.btn-outline-primary{min-width:200px;margin-bottom:1rem;margin-right:1rem}.new-type-input{margin-bottom:1rem}.new-type-text{margin-bottom:.5rem}\n"] }]
2253
+ }], ctorParameters: function () { return [{ type: i1$2.FormBuilder }, { type: i2.ZoneService }, { type: i2.LayerService }, { type: i2.BaseUserService }, { type: i2.NavigationService }, { type: i2.PlanService }]; }, propDecorators: { zoneEdit: [{
2254
+ type: Input
2255
+ }], spaceID: [{
2256
+ type: Input
2257
+ }], images360: [{
2258
+ type: Input
2259
+ }], navigationIDs: [{
2260
+ type: Input
2261
+ }], zones: [{
2262
+ type: Input
2263
+ }], newZoneData: [{
2264
+ type: Input
2265
+ }], updatedZone: [{
2266
+ type: Output
2267
+ }], chosenPlan: [{
2268
+ type: Input
2269
+ }], isMuseumVisit: [{
2270
+ type: Input
2271
+ }], defaultZone: [{
2272
+ type: Input
2273
+ }] } });
2274
+
2275
+ class ZonesComponent {
2276
+ constructor(route, zoneService, spaceService, visitService, navigationService, userService, planService, translate) {
2277
+ this.route = route;
2278
+ this.zoneService = zoneService;
2279
+ this.spaceService = spaceService;
2280
+ this.visitService = visitService;
2281
+ this.navigationService = navigationService;
2282
+ this.userService = userService;
2283
+ this.planService = planService;
2284
+ this.translate = translate;
2285
+ this.lotIndexDetails = -1;
2286
+ this.updatedZone = new EventEmitter();
2287
+ this.isEditingZone = false;
2288
+ this.carouselIsVisible = false;
2289
+ this.planIsVisible = false;
2290
+ this.loadingPlan = false;
2291
+ this.menuItems = [];
2292
+ this.isMuseumVisit = false;
2293
+ this.isGuide = false;
2294
+ this.isAdmin = false;
2295
+ this.floorZones = [];
2296
+ this.zonesMap = new Map();
2297
+ this.floorDetails = -1;
2298
+ this.defaultShowing = false;
2299
+ }
2300
+ async ngOnInit() {
2301
+ this.spaceID = this.route.snapshot.paramMap.get("id");
2302
+ this.setUserPermissions();
2303
+ this.resetZones();
2304
+ }
2305
+ setUserPermissions() {
2306
+ this.isGuide = this.userService.isGuide();
2307
+ this.isAdmin = this.userService.isAdmin(this.spaceID);
2308
+ }
2309
+ setupMenuItems() {
2310
+ this.menuItems = [
2311
+ { label: "Locations", url: "/localisation" },
2312
+ {
2313
+ label: this.currentSpace.name,
2314
+ url: `/localisation/${this.spaceID}`,
2315
+ },
2316
+ {
2317
+ label: "Zones",
2318
+ url: `/localisation/${this.currentSpace.id}/zones`,
2319
+ },
2320
+ ];
2321
+ }
2322
+ onGoBack() {
2323
+ this.editCompleted(null);
2324
+ }
2325
+ ngOnChanges() {
2326
+ this.resetZones();
2327
+ }
2328
+ async resetZones() {
2329
+ this.isMuseumVisit = false;
2330
+ this.currentSpace = await this.spaceService.getSpace(this.spaceID);
2331
+ this.setupMenuItems();
2332
+ this.allZones = await this.zoneService.getZonesBySpace(this.spaceID);
2333
+ this.defaultZone = this.allZones.find((zone) => zone.layer && zone.layer.name === "BUILDING" && zone.sweepIDs);
2334
+ this.floorZones = this.allZones.filter((zone) => zone.layer && zone.layer.name === "FLOOR");
2335
+ // we add only children zones that are not Floors
2336
+ this.zonesMap.set(this.defaultZone.id, this.allZones.filter((zone) => zone.parentID === this.defaultZone.id && !this.floorZones.includes(zone)));
2337
+ for (const floor of this.floorZones) {
2338
+ this.zonesMap.set(floor.id, this.allZones.filter((zone) => zone.parentID === floor.id));
2339
+ }
2340
+ // this.zones = sortAlphabeticallyOnName(this.zones);
2341
+ this.resetNavigations();
2342
+ this.images360 = await this.visitService.loadImagesForSpace(this.currentSpace);
2343
+ this.plans = await this.planService.getPlansWithZonesForSpace(this.spaceID);
2344
+ this.plans = this.plans.filter((plan) => plan.isCurrentForZone);
2345
+ this.onPlansLoaded();
2346
+ }
2347
+ resetNavigations() {
2348
+ this.navigations = [];
2349
+ const zonesWithSweeps = this.allZones.filter((zone) => zone.sweepIDs);
2350
+ this.navigations = [
2351
+ ...new Set(zonesWithSweeps.flatMap((zone) => zone.sweepIDs)),
2352
+ ];
2353
+ }
2354
+ async onPlansLoaded() {
2355
+ if (this.planIsVisible && this.currentZone) {
2356
+ this.preparePlan();
2357
+ }
2358
+ }
2359
+ /**
2360
+ * onToggleDetail folds/unfolds details for a zone
2361
+ * @param i index of the lot to be toggled
2362
+ */
2363
+ async onToggleDetail(index, zone) {
2364
+ this.lotIndexDetails = index === this.lotIndexDetails ? -1 : index;
2365
+ if (this.lotIndexDetails === -1) {
2366
+ return;
2367
+ }
2368
+ this.currentZone = zone;
2369
+ this.planIsVisible = true;
2370
+ if (!this.plans) {
2371
+ return; // wait until the plans are set
2372
+ }
2373
+ if (this.loadingPlan) {
2374
+ // another plan is already loading
2375
+ await wait(1000);
2376
+ }
2377
+ this.preparePlan();
2378
+ }
2379
+ onToggleDetailFloor(index, floor) {
2380
+ this.defaultShowing = false;
2381
+ this.floorDetails = index === this.floorDetails ? -1 : index;
2382
+ if (this.floorDetails === -1) {
2383
+ return;
2384
+ }
2385
+ this.currentZone = floor;
2386
+ this.planIsVisible = true;
2387
+ if (!this.plans) {
2388
+ console.log("plans not ready");
2389
+ return; // wait until the plans are set
2390
+ }
2391
+ this.preparePlan();
2392
+ }
2393
+ onToggleDefaultZone() {
2394
+ this.defaultShowing = !this.defaultShowing;
2395
+ this.currentZone = this.defaultShowing ? this.defaultZone : null;
2396
+ }
2397
+ async preparePlan() {
2398
+ this.loadingPlan = true;
2399
+ this.currentPlan = null;
2400
+ if (this.currentZone.virtual) {
2401
+ this.currentPlan = null;
2402
+ this.loadingPlan = false;
2403
+ return;
2404
+ }
2405
+ this.currentPlan = await this.planService.getCalibratedPlanForZone(this.currentZone);
2406
+ if (this.currentPlan) {
2407
+ const scans = await this.navigationService.getNavigationsForZone(this.currentZone);
2408
+ await this.showScanPointsOnPlan(scans);
2409
+ }
2410
+ this.loadingPlan = false;
2411
+ }
2412
+ toggleCarousel() {
2413
+ this.carouselIsVisible = true;
2414
+ }
2415
+ onEdit(zone) {
2416
+ this.zoneForEdit = zone;
2417
+ this.isEditingZone = true;
2418
+ this.menuItems.push({
2419
+ label: `${zone.name}`,
2420
+ });
2421
+ }
2422
+ onAddLot() {
2423
+ this.isMuseumVisit = false;
2424
+ this.isEditingZone = true;
2425
+ this.menuItems.push({
2426
+ label: "New",
2427
+ });
2428
+ }
2429
+ onAddMuseumZone() {
2430
+ this.isMuseumVisit = true;
2431
+ this.isEditingZone = true;
2432
+ this.menuItems.push({
2433
+ label: "New Museum Itinerary",
2434
+ });
2435
+ }
2436
+ async onDelete(zone, floorID) {
2437
+ const message = this.translate.instant("confirm.deleteZone");
2438
+ // eslint-disable-next-line no-alert
2439
+ if (window.confirm(message)) {
2440
+ await this.zoneService.deleteZone(zone.id);
2441
+ this.allZones.splice(this.allZones.indexOf(zone), 1);
2442
+ const floorZones = this.zonesMap.get(floorID);
2443
+ floorZones.splice(floorZones.indexOf(zone), 1);
2444
+ this.zonesMap.set(floorID, floorZones);
2445
+ }
2446
+ }
2447
+ editCompleted(event) {
2448
+ this.lotIndexDetails = -1;
2449
+ if (event) {
2450
+ this.resetZones();
2451
+ }
2452
+ this.isEditingZone = false;
2453
+ this.zoneForEdit = null;
2454
+ this.planIsVisible = false;
2455
+ this.carouselIsVisible = false;
2456
+ this.setupMenuItems();
2457
+ }
2458
+ async showScanPointsOnPlan(navigations) {
2459
+ const divPlan = document.querySelector("#planDiv");
2460
+ await showScanPointsOnPlanInDiv(this.currentPlan, divPlan, navigations);
2461
+ this.loadingPlan = false;
2462
+ panzoom(divPlan, {
2463
+ bounds: true,
2464
+ boundsPadding: 0,
2465
+ maxZoom: 3.5,
2466
+ });
2467
+ }
2468
+ }
2469
+ ZonesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ZonesComponent, deps: [{ token: i1.ActivatedRoute }, { token: i2.ZoneService }, { token: i2.SpaceService }, { token: i2.VisitService }, { token: i2.NavigationService }, { token: i2.BaseUserService }, { token: i2.PlanService }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
2470
+ ZonesComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: ZonesComponent, selector: "lib-zones", outputs: { updatedZone: "updatedZone" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\">\r\n <div class=\"m-3\">\r\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col-md-6\">\r\n <ul class=\"list-group list-group-flush\" *ngIf=\"!isEditingZone\">\r\n <li class=\"list-group-item default-zone-line-item\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\" *ngIf=\"defaultZone\">\r\n {{defaultZone.name}}\r\n <div class=\"default-zone-label\">{{ \"whole space\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"defaultShowing\" (click)=\"onToggleDefaultZone()\"></lib-chevron>\r\n </div>\r\n <div *ngIf=\"defaultShowing\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\">{{'Type of zone' | translate}}: {{currentZone.layer ?\r\n currentZone.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"currentZone.surface\">{{'Surface' | translate}}, m<sup>2</sup>:\r\n {{currentZone.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!currentZone.virtual\">{{'Scan Points' | translate}}:\r\n {{currentZone.sweepIDs ? currentZone.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n </ul>\r\n <div *ngFor=\"let pair of zonesMap | keyvalue\">\r\n <div *ngIf=\"pair.key == defaultZone.id\">\r\n <h5 style=\"margin: 8px;\" *ngIf=\"pair.value.length > 0\">{{'Children zones'|translate}}</h5>\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item list-group-flush list-group-item-action\"\r\n *ngFor=\"let lot of pair.value; index as zoneIndex\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\">\r\n {{lot.name}}\r\n <div class=\"museum-label\" *ngIf=\"lot.isMuseumVisitZone\">{{ \"itinerary\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"zoneIndex==lotIndexDetails\" (click)=\"onToggleDetail(zoneIndex, lot)\"></lib-chevron>\r\n \r\n </div>\r\n <div *ngIf=\"zoneIndex==lotIndexDetails\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.isMuseumVisitZone\">{{'Type of zone' |\r\n translate}}: {{lot.layer ? lot.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.surface\">{{'Surface' | translate}},\r\n m<sup>2</sup>:\r\n {{lot.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.virtual\">{{'Scan Points' | translate}}:\r\n {{lot.sweepIDs ? lot.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.virtual\">{{'Zone not visible in 3D visit'\r\n |\r\n translate}}</li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n (click)=\"onEdit(lot)\">{{'Edit' |\r\n translate}}</button>\r\n <!-- <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\"\r\n (click)=\"onDelete(lot, floor.id)\">{{'Delete'\r\n | translate}}</button> -->\r\n </div>\r\n </li>\r\n </ul>\r\n \r\n </div>\r\n </div>\r\n </div>\r\n </li>\r\n \r\n <li class=\"list-group-item\" *ngFor=\"let floor of floorZones; index as index\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\">\r\n {{floor.name}}\r\n <!-- <small>({{ lot.metadata ? ('Calibrated' | translate) : ('Not calibrated' | translate)}})</small> -->\r\n <div class=\"museum-label\" *ngIf=\"floor.isMuseumVisitZone\">{{ \"itinerary\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"index==floorDetails\" (click)=\"onToggleDetailFloor(index, floor)\"></lib-chevron>\r\n </div>\r\n <div *ngIf=\"index==floorDetails\"> \r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\">{{'Type of zone' | translate}}: {{floor.layer ?\r\n floor.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"floor.surface\">{{'Surface' | translate}}, m<sup>2</sup>:\r\n {{floor.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!floor.virtual\">{{'Scan Points' | translate}}:\r\n {{floor.sweepIDs ? floor.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"floor.virtual\">{{'Zone not visible in 3D visit' |\r\n translate}}</li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onEdit(floor)\">{{'Edit' |\r\n translate}}</button>\r\n \r\n <!-- List of children zones -->\r\n \r\n <div *ngFor=\"let pair of zonesMap | keyvalue\">\r\n <div *ngIf=\"pair.key == floor.id\">\r\n <h5 style=\"margin: 8px;\" *ngIf=\"pair.value.length > 0\">{{'Children zones'|translate}}</h5>\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item list-group-flush list-group-item-action\"\r\n *ngFor=\"let lot of pair.value; index as zoneIndex\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\">\r\n {{lot.name}}\r\n <div class=\"museum-label\" *ngIf=\"lot.isMuseumVisitZone\">{{ \"itinerary\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"zoneIndex==lotIndexDetails\" (click)=\"onToggleDetail(zoneIndex, lot)\"></lib-chevron>\r\n \r\n </div>\r\n <div *ngIf=\"zoneIndex==lotIndexDetails\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.isMuseumVisitZone\">{{'Type of zone' |\r\n translate}}: {{lot.layer ? lot.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.surface\">{{'Surface' | translate}},\r\n m<sup>2</sup>:\r\n {{lot.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.virtual\">{{'Scan Points' | translate}}:\r\n {{lot.sweepIDs ? lot.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.virtual\">{{'Zone not visible in 3D visit'\r\n |\r\n translate}}</li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n (click)=\"onEdit(lot)\">{{'Edit' |\r\n translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\"\r\n (click)=\"onDelete(lot, floor.id)\">{{'Delete'\r\n | translate}}</button>\r\n </div>\r\n </li>\r\n </ul>\r\n \r\n </div>\r\n </div>\r\n </div>\r\n </li>\r\n </ul>\r\n \r\n <div *ngIf=\"!isEditingZone\">\r\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onAddLot()\">{{'Add zone' |\r\n translate}}</button>\r\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onAddMuseumZone()\"\r\n *ngIf=\"isGuide || isAdmin\">\r\n {{'Add museum itinerary' | translate}}</button>\r\n </div>\r\n </div>\r\n <div class=\"col-md-6\" *ngIf=\"!isEditingZone\">\r\n <div class=\"d-flex justify-content-center\" *ngIf=\"loadingPlan\">\r\n <div class=\"spinner-border\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading...</span>\r\n </div>\r\n </div>\r\n <div class=\"row\" style=\"overflow: hidden;\" *ngIf=\"currentZone\">\r\n <div class=\"mt-3 ms-3\" style=\"height: 500px; width: 100%;\" id=\"planDiv\" *ngIf=\"currentPlan\"></div>\r\n <div class=\"mt-3 ms-3\" id=\"no-plan\" *ngIf=\"!currentPlan && plans && !currentZone.virtual && !loadingPlan\">\r\n {{ 'Plan is not calibrated' | translate}}\r\n </div>\r\n <div class=\"mt-3 ms-3\" id=\"no-plan\" *ngIf=\"currentZone && currentZone.virtual\">\r\n {{ 'No scan points chosen' | translate}}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mb-3\" *ngIf=\"isEditingZone\">\r\n <lib-add-zone [zoneEdit]=\"zoneForEdit\" [spaceID]=\"spaceID\" [images360]=\"images360\" [navigationIDs]=\"navigations\"\r\n [zones]=\"allZones\" [defaultZone]=\"defaultZone\" (updatedZone)=\"editCompleted($event)\" [isMuseumVisit]=\"isMuseumVisit\"></lib-add-zone>\r\n </div>\r\n </div>\r\n", styles: [".museum-label{background-color:#6f3974;font-size:.95rem;border-radius:10px;padding:5px 10px;color:#fff;text-transform:uppercase;margin-left:auto;margin-right:8px}.default-zone-line-item{border-bottom-width:3px}.default-zone-label{background-color:var(--smarterplan-primary);font-size:.95rem;border-radius:10px;padding:5px 10px;color:#fff;text-transform:uppercase;margin-left:auto;margin-right:8px}\n"], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }, { type: ChevronComponent, selector: "lib-chevron", inputs: ["conditionShowing"] }, { type: AddZoneComponent, selector: "lib-add-zone", inputs: ["zoneEdit", "spaceID", "images360", "navigationIDs", "zones", "newZoneData", "chosenPlan", "isMuseumVisit", "defaultZone"], outputs: ["updatedZone"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "translate": i3.TranslatePipe, "keyvalue": i5.KeyValuePipe } });
2471
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ZonesComponent, decorators: [{
2472
+ type: Component,
2473
+ args: [{ selector: 'lib-zones', template: "<div class=\"container-fluid\" *ngIf=\"currentSpace\">\r\n <div class=\"m-3\">\r\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col-md-6\">\r\n <ul class=\"list-group list-group-flush\" *ngIf=\"!isEditingZone\">\r\n <li class=\"list-group-item default-zone-line-item\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\" *ngIf=\"defaultZone\">\r\n {{defaultZone.name}}\r\n <div class=\"default-zone-label\">{{ \"whole space\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"defaultShowing\" (click)=\"onToggleDefaultZone()\"></lib-chevron>\r\n </div>\r\n <div *ngIf=\"defaultShowing\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\">{{'Type of zone' | translate}}: {{currentZone.layer ?\r\n currentZone.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"currentZone.surface\">{{'Surface' | translate}}, m<sup>2</sup>:\r\n {{currentZone.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!currentZone.virtual\">{{'Scan Points' | translate}}:\r\n {{currentZone.sweepIDs ? currentZone.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n </ul>\r\n <div *ngFor=\"let pair of zonesMap | keyvalue\">\r\n <div *ngIf=\"pair.key == defaultZone.id\">\r\n <h5 style=\"margin: 8px;\" *ngIf=\"pair.value.length > 0\">{{'Children zones'|translate}}</h5>\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item list-group-flush list-group-item-action\"\r\n *ngFor=\"let lot of pair.value; index as zoneIndex\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\">\r\n {{lot.name}}\r\n <div class=\"museum-label\" *ngIf=\"lot.isMuseumVisitZone\">{{ \"itinerary\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"zoneIndex==lotIndexDetails\" (click)=\"onToggleDetail(zoneIndex, lot)\"></lib-chevron>\r\n \r\n </div>\r\n <div *ngIf=\"zoneIndex==lotIndexDetails\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.isMuseumVisitZone\">{{'Type of zone' |\r\n translate}}: {{lot.layer ? lot.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.surface\">{{'Surface' | translate}},\r\n m<sup>2</sup>:\r\n {{lot.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.virtual\">{{'Scan Points' | translate}}:\r\n {{lot.sweepIDs ? lot.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.virtual\">{{'Zone not visible in 3D visit'\r\n |\r\n translate}}</li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n (click)=\"onEdit(lot)\">{{'Edit' |\r\n translate}}</button>\r\n <!-- <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\"\r\n (click)=\"onDelete(lot, floor.id)\">{{'Delete'\r\n | translate}}</button> -->\r\n </div>\r\n </li>\r\n </ul>\r\n \r\n </div>\r\n </div>\r\n </div>\r\n </li>\r\n \r\n <li class=\"list-group-item\" *ngFor=\"let floor of floorZones; index as index\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\">\r\n {{floor.name}}\r\n <!-- <small>({{ lot.metadata ? ('Calibrated' | translate) : ('Not calibrated' | translate)}})</small> -->\r\n <div class=\"museum-label\" *ngIf=\"floor.isMuseumVisitZone\">{{ \"itinerary\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"index==floorDetails\" (click)=\"onToggleDetailFloor(index, floor)\"></lib-chevron>\r\n </div>\r\n <div *ngIf=\"index==floorDetails\"> \r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\">{{'Type of zone' | translate}}: {{floor.layer ?\r\n floor.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"floor.surface\">{{'Surface' | translate}}, m<sup>2</sup>:\r\n {{floor.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!floor.virtual\">{{'Scan Points' | translate}}:\r\n {{floor.sweepIDs ? floor.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"floor.virtual\">{{'Zone not visible in 3D visit' |\r\n translate}}</li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onEdit(floor)\">{{'Edit' |\r\n translate}}</button>\r\n \r\n <!-- List of children zones -->\r\n \r\n <div *ngFor=\"let pair of zonesMap | keyvalue\">\r\n <div *ngIf=\"pair.key == floor.id\">\r\n <h5 style=\"margin: 8px;\" *ngIf=\"pair.value.length > 0\">{{'Children zones'|translate}}</h5>\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item list-group-flush list-group-item-action\"\r\n *ngFor=\"let lot of pair.value; index as zoneIndex\">\r\n <div class=\"d-flex justify-content-between align-items-center zone-name\">\r\n {{lot.name}}\r\n <div class=\"museum-label\" *ngIf=\"lot.isMuseumVisitZone\">{{ \"itinerary\" | translate }}</div>\r\n <lib-chevron [conditionShowing]=\"zoneIndex==lotIndexDetails\" (click)=\"onToggleDetail(zoneIndex, lot)\"></lib-chevron>\r\n \r\n </div>\r\n <div *ngIf=\"zoneIndex==lotIndexDetails\">\r\n <ul class=\"list-group list-group-flush\">\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.isMuseumVisitZone\">{{'Type of zone' |\r\n translate}}: {{lot.layer ? lot.layer.name :\r\n \"No type\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.surface\">{{'Surface' | translate}},\r\n m<sup>2</sup>:\r\n {{lot.surface }}\r\n </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"!lot.virtual\">{{'Scan Points' | translate}}:\r\n {{lot.sweepIDs ? lot.sweepIDs.length : \"No scan points\" | translate}} </li>\r\n <li class=\"list-group-item bg-transparent\" *ngIf=\"lot.virtual\">{{'Zone not visible in 3D visit'\r\n |\r\n translate}}</li>\r\n </ul>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill me-2\"\r\n (click)=\"onEdit(lot)\">{{'Edit' |\r\n translate}}</button>\r\n <button type=\"button\" class=\"btn btn-outline-primary rounded-pill\"\r\n (click)=\"onDelete(lot, floor.id)\">{{'Delete'\r\n | translate}}</button>\r\n </div>\r\n </li>\r\n </ul>\r\n \r\n </div>\r\n </div>\r\n </div>\r\n </li>\r\n </ul>\r\n \r\n <div *ngIf=\"!isEditingZone\">\r\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onAddLot()\">{{'Add zone' |\r\n translate}}</button>\r\n <button class=\"btn btn-outline-primary rounded-pill me-2\" (click)=\"onAddMuseumZone()\"\r\n *ngIf=\"isGuide || isAdmin\">\r\n {{'Add museum itinerary' | translate}}</button>\r\n </div>\r\n </div>\r\n <div class=\"col-md-6\" *ngIf=\"!isEditingZone\">\r\n <div class=\"d-flex justify-content-center\" *ngIf=\"loadingPlan\">\r\n <div class=\"spinner-border\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading...</span>\r\n </div>\r\n </div>\r\n <div class=\"row\" style=\"overflow: hidden;\" *ngIf=\"currentZone\">\r\n <div class=\"mt-3 ms-3\" style=\"height: 500px; width: 100%;\" id=\"planDiv\" *ngIf=\"currentPlan\"></div>\r\n <div class=\"mt-3 ms-3\" id=\"no-plan\" *ngIf=\"!currentPlan && plans && !currentZone.virtual && !loadingPlan\">\r\n {{ 'Plan is not calibrated' | translate}}\r\n </div>\r\n <div class=\"mt-3 ms-3\" id=\"no-plan\" *ngIf=\"currentZone && currentZone.virtual\">\r\n {{ 'No scan points chosen' | translate}}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"mb-3\" *ngIf=\"isEditingZone\">\r\n <lib-add-zone [zoneEdit]=\"zoneForEdit\" [spaceID]=\"spaceID\" [images360]=\"images360\" [navigationIDs]=\"navigations\"\r\n [zones]=\"allZones\" [defaultZone]=\"defaultZone\" (updatedZone)=\"editCompleted($event)\" [isMuseumVisit]=\"isMuseumVisit\"></lib-add-zone>\r\n </div>\r\n </div>\r\n", styles: [".museum-label{background-color:#6f3974;font-size:.95rem;border-radius:10px;padding:5px 10px;color:#fff;text-transform:uppercase;margin-left:auto;margin-right:8px}.default-zone-line-item{border-bottom-width:3px}.default-zone-label{background-color:var(--smarterplan-primary);font-size:.95rem;border-radius:10px;padding:5px 10px;color:#fff;text-transform:uppercase;margin-left:auto;margin-right:8px}\n"] }]
2474
+ }], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i2.ZoneService }, { type: i2.SpaceService }, { type: i2.VisitService }, { type: i2.NavigationService }, { type: i2.BaseUserService }, { type: i2.PlanService }, { type: i3.TranslateService }]; }, propDecorators: { updatedZone: [{
2475
+ type: Output
2476
+ }] } });
2477
+
2478
+ class ImagesComponent {
2479
+ constructor(route, spaceService, visitService, nodeService) {
2480
+ this.route = route;
2481
+ this.spaceService = spaceService;
2482
+ this.visitService = visitService;
2483
+ this.nodeService = nodeService;
2484
+ this.loading = false;
2485
+ this.menuItems = [];
2486
+ }
2487
+ ngOnInit() {
2488
+ this.spaceID = this.route.snapshot.paramMap.get("id");
2489
+ this.loadImages();
2490
+ }
2491
+ setupMenuItems() {
2492
+ this.menuItems = [
2493
+ { label: "Locations", url: "/localisation" },
2494
+ {
2495
+ label: this.currentSpace.name,
2496
+ url: `/localisation/${this.spaceID}`,
2497
+ },
2498
+ { label: "360° Images" },
2499
+ ];
2500
+ if (this.currentVisit) {
2501
+ this.menuItems.push({ label: this.currentVisit.name });
2502
+ }
2503
+ }
2504
+ onGoBack() {
2505
+ this.currentVisit = null;
2506
+ this.currentImages = null;
2507
+ this.setupMenuItems();
2508
+ }
2509
+ async loadImages() {
2510
+ this.loading = true;
2511
+ this.currentSpace = await this.spaceService.getSpace(this.spaceID);
2512
+ this.setupMenuItems();
2513
+ this.images = await this.visitService.loadImagesForSpace(this.currentSpace);
2514
+ if (this.currentSpace.visits.length === 1) {
2515
+ this.setVisit(this.currentSpace.visits[0]);
2516
+ }
2517
+ this.loading = false;
2518
+ }
2519
+ setVisit(visit) {
2520
+ this.currentVisit = visit;
2521
+ this.currentImages = this.images.filter((im) => {
2522
+ return im.visit === visit;
2523
+ });
2524
+ this.setupMenuItems();
2525
+ }
2526
+ setCurrentScan(id) {
2527
+ this.currentScanID = id;
2528
+ }
2529
+ async onInventoryClick() {
2530
+ const { node } = this.currentVisit;
2531
+ const { filename } = this.currentImages[this.currentScanID]; // 110ee452_sweep.jpeg
2532
+ // check if node has already errors of inventory
2533
+ const missingFilesErrors = [
2534
+ InventoryStatus.ERROR_FILE_JSON_NOT_EXIST,
2535
+ InventoryStatus.ERROR_FILE_MODEL_NOT_EXIST,
2536
+ InventoryStatus.ERROR_FILE_OBJ_NOT_EXIST,
2537
+ ];
2538
+ if (node.inventoryStatus &&
2539
+ missingFilesErrors.includes(node.inventoryStatus)) {
2540
+ alert("Files missing for this visit's inventory. Contact your administrator.");
2541
+ }
2542
+ else {
2543
+ await this.nodeService.updateNode({
2544
+ id: node.id,
2545
+ inventoryStatus: InventoryStatus.TO_RUN_ONE_IMAGE,
2546
+ navigationForInventory: filename.split("_")[0],
2547
+ });
2548
+ alert("Automatic AI Inventory started! Detected equipments will appear soon in the visit.");
2549
+ }
2550
+ }
2551
+ }
2552
+ ImagesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ImagesComponent, deps: [{ token: i1.ActivatedRoute }, { token: i2.SpaceService }, { token: i2.VisitService }, { token: i2.NodeService }], target: i0.ɵɵFactoryTarget.Component });
2553
+ ImagesComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: ImagesComponent, selector: "lib-images", ngImport: i0, template: "<div class=\"container-fluid dashboard-tab col-sm-10 mb-3\" *ngIf=\"currentSpace\" >\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">{{'Loading' | translate}}...</span>\n </div>\n </div>\n <div ngbDropdown class=\"d-inline-block\" *ngIf=\"currentSpace.visits && currentSpace.visits.length > 0\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" *ngIf=\"!currentVisit\"\n ngbDropdownToggle>{{'Choose 3D visit' | translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let visit of currentSpace.visits\" (click)=\"setVisit(visit)\"> {{ visit.name }}\n </button>\n </div>\n </div>\n <h3 *ngIf=\"currentVisit\">{{ currentVisit.name }}</h3>\n <div class=\"mt-3\" *ngIf=\"currentVisit && currentImages.length == 0 && !loading\">\n <h4>{{'No imported images' | translate }}</h4>\n <p>{{'For import: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <button *ngIf=\"currentVisit && currentImages.length > 0 && !loading\"\n class=\"btn btn-outline-primary rounded-pill no-lowercase mb-2\" (click)=\"onInventoryClick()\">\n {{'Run AI inventory on this image' | translate}}</button>\n <lib-carousel [images]=\"currentImages\" *ngIf=\"currentImages\" (currentScan)=\"setCurrentScan($event)\"></lib-carousel>\n </div>", styles: [""], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }, { type: CarouselComponent, selector: "lib-carousel", inputs: ["images"], outputs: ["currentScan"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { type: i7.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { type: i7.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["disabled"] }], pipes: { "translate": i3.TranslatePipe } });
2554
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: ImagesComponent, decorators: [{
2555
+ type: Component,
2556
+ args: [{ selector: 'lib-images', template: "<div class=\"container-fluid dashboard-tab col-sm-10 mb-3\" *ngIf=\"currentSpace\" >\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems' (onGoBack)=\"onGoBack()\"></lib-tab-navigation>\n </div>\n <div class=\"d-flex justify-content-center\" *ngIf=\"loading\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">{{'Loading' | translate}}...</span>\n </div>\n </div>\n <div ngbDropdown class=\"d-inline-block\" *ngIf=\"currentSpace.visits && currentSpace.visits.length > 0\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" *ngIf=\"!currentVisit\"\n ngbDropdownToggle>{{'Choose 3D visit' | translate}}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem *ngFor=\"let visit of currentSpace.visits\" (click)=\"setVisit(visit)\"> {{ visit.name }}\n </button>\n </div>\n </div>\n <h3 *ngIf=\"currentVisit\">{{ currentVisit.name }}</h3>\n <div class=\"mt-3\" *ngIf=\"currentVisit && currentImages.length == 0 && !loading\">\n <h4>{{'No imported images' | translate }}</h4>\n <p>{{'For import: go Virtual Visits => Import Images' | translate}}</p>\n </div>\n <button *ngIf=\"currentVisit && currentImages.length > 0 && !loading\"\n class=\"btn btn-outline-primary rounded-pill no-lowercase mb-2\" (click)=\"onInventoryClick()\">\n {{'Run AI inventory on this image' | translate}}</button>\n <lib-carousel [images]=\"currentImages\" *ngIf=\"currentImages\" (currentScan)=\"setCurrentScan($event)\"></lib-carousel>\n </div>", styles: [""] }]
2557
+ }], ctorParameters: function () { return [{ type: i1.ActivatedRoute }, { type: i2.SpaceService }, { type: i2.VisitService }, { type: i2.NodeService }]; } });
2558
+
2559
+ class EditPlanComponent {
2560
+ constructor(planService, route, router, modalService, zoneService, visitService, spaceService, translate) {
2561
+ this.planService = planService;
2562
+ this.route = route;
2563
+ this.router = router;
2564
+ this.modalService = modalService;
2565
+ this.zoneService = zoneService;
2566
+ this.visitService = visitService;
2567
+ this.spaceService = spaceService;
2568
+ this.translate = translate;
2569
+ this.loading = false;
2570
+ this.isNewZone = false;
2571
+ /**
2572
+ * Options for svgEditor
2573
+ */
2574
+ this.config = {
2575
+ noStorageOnLoad: true,
2576
+ forceStorage: true,
2577
+ // imgPath: "/assets/editor/images/",
2578
+ // extPath: "/assets/editor/extensions/",
2579
+ // langPath: "/assets/editor/locale/",
2580
+ // canvgPath: "/assets/editor/canvg/",
2581
+ // jspdfPath: "/assets/editor/jspdf/",
2582
+ // jGraduatePath: "/assets/editor/jgraduate/images/",
2583
+ // extIconsPath: "/assets/editor/extensions/",
2584
+ // stylesheets: [
2585
+ // "/assets/editor/jgraduate/css/jGraduate.css",
2586
+ // "/assets/editor/spinbtn/jQuery.SpinButton.css",
2587
+ // "/assets/editor/jgraduate/css/jPicker.css",
2588
+ // "/assets/editor/svg-editor.css",
2589
+ // ],
2590
+ dimensions: [10000, 10000],
2591
+ showlayers: true,
2592
+ noDefaultExtensions: true,
2593
+ basePath: "/assets/editor/",
2594
+ customExportImage: true,
2595
+ extensions: [
2596
+ "ext-connector",
2597
+ // "ext-eyedropper",
2598
+ // "ext-grid",
2599
+ // "ext-markers",
2600
+ // "ext-overview_window",
2601
+ // "ext-spi-library",
2602
+ // "ext-spi-icon",
2603
+ "ext-sp-zone",
2604
+ // "ext-arrows",
2605
+ // "ext-sp-css",
2606
+ // "ext-placemark",
2607
+ ],
2608
+ };
2609
+ this.menuItems = [];
2610
+ this.chosenPlan = this.planService.getChosenPlan();
2611
+ }
2612
+ async ngOnInit() {
2613
+ this.editorUrl = "/assets/editor/main.html";
2614
+ this.spaceID = this.route.snapshot.queryParams.spaceID;
2615
+ this.currentSpace = await this.spaceService.getSpace(this.spaceID);
2616
+ this.setupMenuItems();
2617
+ if (this.spaceID && !this.planService.getChosenPlan()) {
2618
+ this.router.navigate([
2619
+ "/dashboard/localisation",
2620
+ this.spaceID,
2621
+ "plans",
2622
+ ]);
2623
+ }
2624
+ else if (!this.spaceID && !this.planService.getChosenPlan()) {
2625
+ this.router.navigate(["/dashboard/localisation"]);
2626
+ }
2627
+ }
2628
+ setupMenuItems() {
2629
+ this.menuItems = [
2630
+ { label: "Locations", url: "/localisation" },
2631
+ {
2632
+ label: this.currentSpace.name,
2633
+ url: `/localisation/${this.spaceID}`,
2634
+ },
2635
+ {
2636
+ label: "Plans",
2637
+ url: `/localisation/${this.currentSpace.id}/plans`,
2638
+ },
2639
+ {
2640
+ label: "Edit",
2641
+ url: `/localisation/${this.currentSpace.id}/plan-edit?spaceID=${this.currentSpace.id}`,
2642
+ },
2643
+ ];
2644
+ }
2645
+ async switchAction(event) {
2646
+ if (event.origin === window.location.origin) {
2647
+ switch (event.data.message) {
2648
+ case "save":
2649
+ this.onSave(event.data.redirect);
2650
+ break;
2651
+ case "close":
2652
+ this.onCancel();
2653
+ break;
2654
+ case "new-zone":
2655
+ const modalReference = this.modalService.open(this.loadingModal);
2656
+ const svg = unescape(encodeURIComponent(this.svgEditor.svgCanvas.getSvgString()));
2657
+ const base64 = `data:image/svg+xml;base64,${window.btoa(svg)}`;
2658
+ const currentSpace = await this.spaceService.getSpace(this.spaceID);
2659
+ this.images360 = await this.visitService.loadImagesForSpace(currentSpace);
2660
+ this.editedPlan = { ...this.chosenPlan };
2661
+ this.editedPlan.filepath = base64;
2662
+ this.newZoneData = event.data.zoneData;
2663
+ if (this.newZoneData.elements.length === 0) {
2664
+ modalReference.close();
2665
+ alert(this.translate.instant("edition.no-zone"));
2666
+ break;
2667
+ }
2668
+ this.zones = await this.zoneService.getZonesBySpace(this.spaceID);
2669
+ this.zones = sortAlphabeticallyOnName(this.zones);
2670
+ this.editedZone = this.zones.find((z) => z.name === this.newZoneData.name);
2671
+ this.resetNavigations();
2672
+ modalReference.close();
2673
+ this.modalService.open(this.dialogZone);
2674
+ break;
2675
+ default:
2676
+ break;
2677
+ }
2678
+ }
2679
+ }
2680
+ async resetNavigations() {
2681
+ this.navigations = [];
2682
+ const defaultZone = this.zones.find((zone) => !zone.parentID);
2683
+ if (defaultZone.sweepIDs.length > 0) {
2684
+ this.navigations = defaultZone.sweepIDs;
2685
+ }
2686
+ }
2687
+ async onLoad(svgEditor) {
2688
+ if (svgEditor.contentWindow.SvgEditor) {
2689
+ this.svgEditorIframe = svgEditor;
2690
+ this.svgEditor = new svgEditor.contentWindow.SvgEditor();
2691
+ await this.svgEditor.init();
2692
+ // await this.svgEditor.pref(key, val);
2693
+ // await this.svgEditor.setConfig({ extentions: this.extensions });
2694
+ await this.svgEditor.setConfig(this.config, {
2695
+ allowInitialUserOverride: true,
2696
+ });
2697
+ if (this.chosenPlan && this.chosenPlan.extension === "svg") {
2698
+ this.loadSvg();
2699
+ }
2700
+ if (this.chosenPlan && this.chosenPlan.extension === "pdf") {
2701
+ this.loadPdf();
2702
+ }
2703
+ if (!this.chosenPlan.calibration || !this.chosenPlan.isModified) {
2704
+ setTimeout(() => {
2705
+ this.svgEditorIframe.contentWindow.postMessage({ action: "disable-zone" }, "*");
2706
+ }, 1000);
2707
+ }
2708
+ }
2709
+ }
2710
+ async onSave(redirect) {
2711
+ const imgBlob = await new Blob([this.svgEditor.svgCanvas.getSvgString()], {
2712
+ type: "image/svg+xml",
2713
+ });
2714
+ const [name] = this.chosenPlan.name.split(".");
2715
+ const imgFile = new File([imgBlob], `${name}-edited.svg`, {
2716
+ type: "image/svg+xml",
2717
+ lastModified: Date.now(),
2718
+ });
2719
+ this.planService.setPlanFileCache(imgFile);
2720
+ if (this.chosenPlan.isModified) {
2721
+ this.modalService.open(this.dialog);
2722
+ }
2723
+ else {
2724
+ this.router.navigate([
2725
+ "/dashboard/localisation",
2726
+ this.spaceID,
2727
+ "plans",
2728
+ ]);
2729
+ }
2730
+ }
2731
+ async updateSvg() {
2732
+ const newUrl = await this.planService.updatePlanFile();
2733
+ await this.planService.updatePlan({
2734
+ id: this.chosenPlan.id,
2735
+ annexe: newUrl,
2736
+ });
2737
+ this.planService.setPlanFileCache(null);
2738
+ this.router.navigate([
2739
+ "/dashboard/localisation",
2740
+ this.spaceID,
2741
+ "plans",
2742
+ ]);
2743
+ }
2744
+ async newSvg() {
2745
+ this.router.navigate([
2746
+ "/dashboard/localisation",
2747
+ this.spaceID,
2748
+ "plans",
2749
+ ]);
2750
+ }
2751
+ async onCancel() {
2752
+ this.router.navigate([
2753
+ "/dashboard/localisation",
2754
+ this.spaceID,
2755
+ "plans",
2756
+ ]);
2757
+ alert(this.translate.instant("edition.cancel"));
2758
+ }
2759
+ async loadSvg() {
2760
+ const svgRequest = await downloadFileAsObject(this.chosenPlan.annexe);
2761
+ if (svgRequest) {
2762
+ const svgBlob = await svgRequest.Body;
2763
+ const reader = new FileReader();
2764
+ reader.onloadend = () => {
2765
+ this.svgEditor.loadFromDataURI(reader.result);
2766
+ };
2767
+ reader.readAsDataURL(svgBlob);
2768
+ }
2769
+ }
2770
+ async loadPdf() {
2771
+ this.svgEditorIframe.contentWindow.postMessage({ action: "disable-zone" }, "*");
2772
+ const canvas = document.querySelector("#canvas4pdf");
2773
+ const context = canvas.getContext("2d");
2774
+ const pdfjs = await import('pdfjs-dist/build/pdf');
2775
+ const pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.entry');
2776
+ pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
2777
+ const pdf = await getDocument(this.chosenPlan.filepath).promise;
2778
+ const page = await pdf.getPage(1);
2779
+ const viewPortParameters = { scale: 1.5 };
2780
+ const viewport = page.getViewport(viewPortParameters);
2781
+ this.svgEditor.svgCanvas.setResolution(viewport.width, viewport.height);
2782
+ canvas.height = viewport.height;
2783
+ canvas.width = viewport.width;
2784
+ const renderContext = {
2785
+ canvasContext: context,
2786
+ viewport,
2787
+ };
2788
+ const renderTask = page.render(renderContext).promise;
2789
+ renderTask.then(() => {
2790
+ const newImage = this.svgEditor.svgCanvas.addSVGElementFromJson({
2791
+ element: "image",
2792
+ attr: {
2793
+ x: 0,
2794
+ y: 0,
2795
+ width: viewport.width,
2796
+ height: viewport.height,
2797
+ id: this.svgEditor.svgCanvas.getNextId(),
2798
+ style: "pointer-events:inherit",
2799
+ },
2800
+ });
2801
+ this.svgEditor.svgCanvas.setHref(newImage, canvas.toDataURL("image/png"));
2802
+ });
2803
+ }
2804
+ async onLoadImg(img) {
2805
+ this.svgEditorIframe.contentWindow.postMessage({ action: "disable-zone" }, "*");
2806
+ const imgRequest = await downloadFileAsObject(this.chosenPlan.annexe);
2807
+ if (imgRequest) {
2808
+ const imgBlob = await imgRequest.Body;
2809
+ const imgWidth = img.naturalWidth;
2810
+ const imgHeight = img.naturalHeight;
2811
+ // await this.svgEditor.setConfig({
2812
+ // dimensions: [imgWidth, imgHeight],
2813
+ // });
2814
+ this.svgEditor.svgCanvas.setResolution(imgWidth, imgHeight);
2815
+ const reader = new FileReader();
2816
+ reader.onloadend = () => {
2817
+ const newImage = this.svgEditor.svgCanvas.addSVGElementFromJson({
2818
+ element: "image",
2819
+ attr: {
2820
+ x: 0,
2821
+ y: 0,
2822
+ width: imgWidth,
2823
+ height: imgHeight,
2824
+ id: this.svgEditor.svgCanvas.getNextId(),
2825
+ style: "pointer-events:inherit",
2826
+ },
2827
+ });
2828
+ this.svgEditor.svgCanvas.setHref(newImage, reader.result);
2829
+ };
2830
+ reader.readAsDataURL(imgBlob);
2831
+ }
2832
+ }
2833
+ onNewZone(event) {
2834
+ this.isNewZone = false;
2835
+ if (event) {
2836
+ this.svgEditorIframe.contentWindow.postMessage({ action: "rename-layer", name: event }, "*");
2837
+ }
2838
+ else {
2839
+ this.svgEditorIframe.contentWindow.postMessage({ action: "cancel-layer" }, "*");
2840
+ }
2841
+ }
2842
+ }
2843
+ EditPlanComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: EditPlanComponent, deps: [{ token: i2.PlanService }, { token: i1.ActivatedRoute }, { token: i1.Router }, { token: i7.NgbModal }, { token: i2.ZoneService }, { token: i2.VisitService }, { token: i2.SpaceService }, { token: i3.TranslateService }], target: i0.ɵɵFactoryTarget.Component });
2844
+ EditPlanComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: EditPlanComponent, selector: "lib-edit-plan", host: { listeners: { "window:message": "switchAction($event)" } }, viewQueries: [{ propertyName: "dialog", first: true, predicate: ["dialog"], descendants: true }, { propertyName: "dialogZone", first: true, predicate: ["dialogZone"], descendants: true }, { propertyName: "loadingModal", first: true, predicate: ["preparing"], descendants: true }], ngImport: i0, template: "<div class=\"row mt-3\">\n <div class=\"col\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems'></lib-tab-navigation>\n </div>\n </div>\n</div>\n<iframe *ngIf=\"editorUrl\" #svgEditor [src]=\"editorUrl | safeUrl\" frameborder=\"0\"\n style=\"height: 100%;width:100%;min-height:750px\" (load)=\"onLoad(svgEditor)\"></iframe>\n<!-- chosenPlan && -->\n<!-- ./../../../../../../assets/svgeditor/index.html -->\n\n<img #imgPng *ngIf=\"chosenPlan && chosenPlan.extension !== 'svg' && svgEditor\" [src]=\"chosenPlan.filepath\"\n (load)=\"onLoadImg(imgPng)\" style=\"display: none;\">\n\n<canvas id=\"canvas4pdf\" style=\"display: none;\"></canvas>\n\n<lib-add-zone *ngIf=\"isNewZone\" [images360]=\"images360\" [spaceID]=\"spaceID\" [navigationIDs]=\"navigations\"\n [zoneEdit]=\"editedZone\" [zones]=\"zones\" (updatedZone)=\"onNewZone($event)\" [chosenPlan]=\"editedPlan\"\n [newZoneData]=\"newZoneData\"></lib-add-zone>\n\n<ng-template #dialog let-modal>\n\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">{{'Submit' | translate}}</h4>\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n {{'save plan edited' | translate}}\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-dark\" (click)=\"newSvg();modal.close('Close click')\">{{'Yes' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-dark\" (click)=\"updateSvg();modal.close('Close click')\">{{'No' |\n translate}}</button>\n </div>\n\n</ng-template>\n\n<ng-template #dialogZone let-modal>\n\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">{{'Submit' | translate}}</h4>\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n {{'Select zone' | translate}}\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{editedZone ?\n editedZone.name: ('New zone' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem (click)=\"editedZone = null\" translate=\"New zone\">\n </button>\n <button ngbDropdownItem *ngFor=\"let zone of zones\" (click)=\"editedZone = zone\">{{zone.name }}\n </button>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-dark\"\n (click)=\"isNewZone = true;modal.close('Close click')\">{{'Validate' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-dark\" (click)=\"modal.close('Close click')\">{{'Cancel' |\n translate}}</button>\n </div>\n\n</ng-template>\n\n<ng-template #preparing let-modal>\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">{{'Processing' | translate}}</h4>\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"d-flex justify-content-center\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n </div>\n\n</ng-template>", styles: ["app-add-zone{position:absolute;top:0;left:0;background-color:#fff;width:100%;height:100%;padding:30px}\n"], components: [{ type: TabNavigationComponent, selector: "lib-tab-navigation", inputs: ["menuItems"], outputs: ["onGoBack"] }, { type: AddZoneComponent, selector: "lib-add-zone", inputs: ["zoneEdit", "spaceID", "images360", "navigationIDs", "zones", "newZoneData", "chosenPlan", "isMuseumVisit", "defaultZone"], outputs: ["updatedZone"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i7.NgbDropdown, selector: "[ngbDropdown]", inputs: ["autoClose", "dropdownClass", "open", "placement", "container", "display"], outputs: ["openChange"], exportAs: ["ngbDropdown"] }, { type: i7.NgbDropdownToggle, selector: "[ngbDropdownToggle]" }, { type: i7.NgbDropdownMenu, selector: "[ngbDropdownMenu]" }, { type: i7.NgbDropdownItem, selector: "[ngbDropdownItem]", inputs: ["disabled"] }, { type: i3.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "safeUrl": i2.SafeUrlPipe, "translate": i3.TranslatePipe } });
2845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: EditPlanComponent, decorators: [{
2846
+ type: Component,
2847
+ args: [{ selector: 'lib-edit-plan', template: "<div class=\"row mt-3\">\n <div class=\"col\">\n <div class=\"m-3\">\n <lib-tab-navigation [menuItems]='menuItems'></lib-tab-navigation>\n </div>\n </div>\n</div>\n<iframe *ngIf=\"editorUrl\" #svgEditor [src]=\"editorUrl | safeUrl\" frameborder=\"0\"\n style=\"height: 100%;width:100%;min-height:750px\" (load)=\"onLoad(svgEditor)\"></iframe>\n<!-- chosenPlan && -->\n<!-- ./../../../../../../assets/svgeditor/index.html -->\n\n<img #imgPng *ngIf=\"chosenPlan && chosenPlan.extension !== 'svg' && svgEditor\" [src]=\"chosenPlan.filepath\"\n (load)=\"onLoadImg(imgPng)\" style=\"display: none;\">\n\n<canvas id=\"canvas4pdf\" style=\"display: none;\"></canvas>\n\n<lib-add-zone *ngIf=\"isNewZone\" [images360]=\"images360\" [spaceID]=\"spaceID\" [navigationIDs]=\"navigations\"\n [zoneEdit]=\"editedZone\" [zones]=\"zones\" (updatedZone)=\"onNewZone($event)\" [chosenPlan]=\"editedPlan\"\n [newZoneData]=\"newZoneData\"></lib-add-zone>\n\n<ng-template #dialog let-modal>\n\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">{{'Submit' | translate}}</h4>\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n {{'save plan edited' | translate}}\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-dark\" (click)=\"newSvg();modal.close('Close click')\">{{'Yes' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-dark\" (click)=\"updateSvg();modal.close('Close click')\">{{'No' |\n translate}}</button>\n </div>\n\n</ng-template>\n\n<ng-template #dialogZone let-modal>\n\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">{{'Submit' | translate}}</h4>\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n {{'Select zone' | translate}}\n <div ngbDropdown class=\"d-inline-block me-3\">\n <button class=\"btn btn-label-file rounded-pill\" id=\"dropdownBasic1\" ngbDropdownToggle>{{editedZone ?\n editedZone.name: ('New zone' | translate) }}</button>\n <div ngbDropdownMenu aria-labelledby=\"dropdownBasic1\">\n <button ngbDropdownItem (click)=\"editedZone = null\" translate=\"New zone\">\n </button>\n <button ngbDropdownItem *ngFor=\"let zone of zones\" (click)=\"editedZone = zone\">{{zone.name }}\n </button>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-outline-dark\"\n (click)=\"isNewZone = true;modal.close('Close click')\">{{'Validate' |\n translate}}</button>\n <button type=\"button\" class=\"btn btn-outline-dark\" (click)=\"modal.close('Close click')\">{{'Cancel' |\n translate}}</button>\n </div>\n\n</ng-template>\n\n<ng-template #preparing let-modal>\n <div class=\"modal-header\">\n <h4 class=\"modal-title\">{{'Processing' | translate}}</h4>\n <button type=\"button\" class=\"close\" aria-label=\"Close\" (click)=\"modal.dismiss('Cross click')\">\n <span aria-hidden=\"true\">&times;</span>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"d-flex justify-content-center\">\n <div class=\"spinner-border\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n </div>\n\n</ng-template>", styles: ["app-add-zone{position:absolute;top:0;left:0;background-color:#fff;width:100%;height:100%;padding:30px}\n"] }]
2848
+ }], ctorParameters: function () { return [{ type: i2.PlanService }, { type: i1.ActivatedRoute }, { type: i1.Router }, { type: i7.NgbModal }, { type: i2.ZoneService }, { type: i2.VisitService }, { type: i2.SpaceService }, { type: i3.TranslateService }]; }, propDecorators: { dialog: [{
2849
+ type: ViewChild,
2850
+ args: ["dialog"]
2851
+ }], dialogZone: [{
2852
+ type: ViewChild,
2853
+ args: ["dialogZone"]
2854
+ }], loadingModal: [{
2855
+ type: ViewChild,
2856
+ args: ["preparing"]
2857
+ }], switchAction: [{
2858
+ type: HostListener,
2859
+ args: ["window:message", ["$event"]]
2860
+ }] } });
2861
+
2862
+ const routes = [
2863
+ {
2864
+ path: "",
2865
+ component: LocationsComponent,
2866
+ },
2867
+ {
2868
+ path: ":id",
2869
+ children: [
2870
+ { path: "", component: DetailLocationComponent },
2871
+ { path: "zones", component: ZonesComponent },
2872
+ { path: "visits", component: VisitsComponent },
2873
+ { path: "plans", component: PlansComponent },
2874
+ {
2875
+ path: "plan-calibration",
2876
+ component: CalibrationComponent,
2877
+ },
2878
+ {
2879
+ path: "plan-edit",
2880
+ component: EditPlanComponent,
2881
+ },
2882
+ { path: "images", component: ImagesComponent },
2883
+ ],
2884
+ },
2885
+ ];
2886
+ class LocationsRoutingModule {
2887
+ }
2888
+ LocationsRoutingModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LocationsRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2889
+ LocationsRoutingModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LocationsRoutingModule, imports: [i1.RouterModule], exports: [RouterModule] });
2890
+ LocationsRoutingModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LocationsRoutingModule, imports: [[RouterModule.forChild(routes)], RouterModule] });
2891
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: LocationsRoutingModule, decorators: [{
2892
+ type: NgModule,
2893
+ args: [{
2894
+ imports: [RouterModule.forChild(routes)],
2895
+ exports: [RouterModule],
2896
+ }]
2897
+ }] });
2898
+
2899
+ class NgxSmarterplanLocationsModule {
2900
+ }
2901
+ NgxSmarterplanLocationsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxSmarterplanLocationsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2902
+ NgxSmarterplanLocationsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxSmarterplanLocationsModule, declarations: [LocationsComponent,
2903
+ DetailLocationComponent,
2904
+ CalibrationComponent,
2905
+ PlansComponent,
2906
+ VisitsComponent,
2907
+ ZonesComponent,
2908
+ ImagesComponent,
2909
+ EditPlanComponent,
2910
+ TabNavigationComponent,
2911
+ MapComponent,
2912
+ MapPopupComponent,
2913
+ FormLocationComponent,
2914
+ ChevronComponent,
2915
+ AddZoneComponent,
2916
+ SelectionComponent,
2917
+ CarouselComponent], imports: [PdfViewerModule,
2918
+ ClipboardModule,
2919
+ NgbModule,
2920
+ CommonModule, i3.TranslateModule, LeafletModule,
2921
+ FormsModule,
2922
+ ReactiveFormsModule,
2923
+ NgxSmarterplanCoreModule,
2924
+ LocationsRoutingModule] });
2925
+ NgxSmarterplanLocationsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxSmarterplanLocationsModule, imports: [[
2926
+ PdfViewerModule,
2927
+ ClipboardModule,
2928
+ NgbModule,
2929
+ CommonModule,
2930
+ TranslateModule.forChild({
2931
+ extend: true
2932
+ }),
2933
+ LeafletModule,
2934
+ FormsModule,
2935
+ ReactiveFormsModule,
2936
+ NgxSmarterplanCoreModule,
2937
+ LocationsRoutingModule,
2938
+ ]] });
2939
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxSmarterplanLocationsModule, decorators: [{
2940
+ type: NgModule,
2941
+ args: [{
2942
+ declarations: [
2943
+ LocationsComponent,
2944
+ DetailLocationComponent,
2945
+ CalibrationComponent,
2946
+ PlansComponent,
2947
+ VisitsComponent,
2948
+ ZonesComponent,
2949
+ ImagesComponent,
2950
+ EditPlanComponent,
2951
+ TabNavigationComponent,
2952
+ MapComponent,
2953
+ MapPopupComponent,
2954
+ FormLocationComponent,
2955
+ ChevronComponent,
2956
+ AddZoneComponent,
2957
+ SelectionComponent,
2958
+ CarouselComponent
2959
+ ],
2960
+ imports: [
2961
+ PdfViewerModule,
2962
+ ClipboardModule,
2963
+ NgbModule,
2964
+ CommonModule,
2965
+ TranslateModule.forChild({
2966
+ extend: true
2967
+ }),
2968
+ LeafletModule,
2969
+ FormsModule,
2970
+ ReactiveFormsModule,
2971
+ NgxSmarterplanCoreModule,
2972
+ LocationsRoutingModule,
2973
+ ],
2974
+ exports: []
2975
+ }]
2976
+ }] });
2977
+
2978
+ /*
2979
+ * Public API Surface of ngx-smarterplan-locations
2980
+ */
2981
+
2982
+ /**
2983
+ * Generated bundle index. Do not edit.
2984
+ */
2985
+
2986
+ export { NgxSmarterplanLocationsModule, NgxSmarterplanLocationsService };
2987
+ //# sourceMappingURL=smarterplan-ngx-smarterplan-locations.mjs.map