@smarterplan/ngx-smarterplan-core 1.4.17 → 1.4.19

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 (99) hide show
  1. package/README.md +24 -24
  2. package/esm2022/lib/components/csv-export/csv-export.component.mjs +3 -3
  3. package/esm2022/lib/components/loader/loader.component.mjs +3 -3
  4. package/esm2022/lib/components/menu-bar/avatar/avatar.component.mjs +1 -1
  5. package/esm2022/lib/components/menu-bar/menu-bar.component.mjs +3 -3
  6. package/esm2022/lib/components/menu-bar/navigation-bar/navigation-bar.component.mjs +3 -3
  7. package/esm2022/lib/components/modal-switch-visit/modal-switch-visit.component.mjs +3 -3
  8. package/esm2022/lib/components/search-bar/search-bar.component.mjs +3 -3
  9. package/esm2022/lib/components/support-modal/support-modal.component.mjs +3 -3
  10. package/esm2022/lib/config.mjs +1 -1
  11. package/esm2022/lib/helpers.service.mjs +1 -1
  12. package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasImage.mjs +1 -1
  13. package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasRenderer.mjs +1 -1
  14. package/esm2022/lib/matterport-extensions/nest-thermostat/NestThermostat.mjs +1 -1
  15. package/esm2022/lib/matterport-extensions/nest-thermostat/PlaneRenderer.mjs +1 -1
  16. package/esm2022/lib/matterport-extensions/scene-component/SceneComponent.mjs +1 -1
  17. package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +22 -22
  18. package/esm2022/lib/matterport-extensions/tv-player/TvPlayer.mjs +1 -1
  19. package/esm2022/lib/matterport-extensions/view-frustum-mesh/ViewFrustumMesh.mjs +22 -22
  20. package/esm2022/lib/mattertagData.mjs +10 -1
  21. package/esm2022/lib/ngx-smarterplan-core.module.mjs +1 -1
  22. package/esm2022/lib/ngx-smarterplan-core.service.mjs +1 -1
  23. package/esm2022/lib/pipes/duration-to-string.pipe.mjs +1 -1
  24. package/esm2022/lib/pipes/format-date-number-to-digits.pipe.mjs +1 -1
  25. package/esm2022/lib/pipes/hashtag-from-id.pipe.mjs +1 -1
  26. package/esm2022/lib/pipes/safe-url.pipe.mjs +1 -1
  27. package/esm2022/lib/pipes/time-date-to-local-string.pipe.mjs +1 -1
  28. package/esm2022/lib/pipes/username-from-id.pipe.mjs +1 -1
  29. package/esm2022/lib/services/amplify-cache.service.mjs +1 -1
  30. package/esm2022/lib/services/base-tab.service.mjs +1 -1
  31. package/esm2022/lib/services/baseVisibility.service.mjs +1 -1
  32. package/esm2022/lib/services/content.service.mjs +1 -1
  33. package/esm2022/lib/services/filter.service.mjs +1 -1
  34. package/esm2022/lib/services/intervention.service.mjs +1 -1
  35. package/esm2022/lib/services/locale.service.mjs +1 -1
  36. package/esm2022/lib/services/matterport-import.service.mjs +10 -3
  37. package/esm2022/lib/services/matterport-measurement.service.mjs +1 -1
  38. package/esm2022/lib/services/matterport-navigation.service.mjs +18 -1
  39. package/esm2022/lib/services/matterport-object3d.service.mjs +1 -1
  40. package/esm2022/lib/services/matterport-pointer.service.mjs +6 -6
  41. package/esm2022/lib/services/matterport-tag-copy.service.mjs +455 -9
  42. package/esm2022/lib/services/matterport-tag.service.mjs +99 -10
  43. package/esm2022/lib/services/matterport.service.mjs +87 -48
  44. package/esm2022/lib/services/models/affectation.service.mjs +1 -1
  45. package/esm2022/lib/services/models/base-object.service.mjs +1 -1
  46. package/esm2022/lib/services/models/capture.service.mjs +1 -1
  47. package/esm2022/lib/services/models/comment.service.mjs +1 -1
  48. package/esm2022/lib/services/models/domain.service.mjs +1 -1
  49. package/esm2022/lib/services/models/equipment.service.mjs +1 -1
  50. package/esm2022/lib/services/models/event.service.mjs +1 -1
  51. package/esm2022/lib/services/models/feature.service.mjs +1 -1
  52. package/esm2022/lib/services/models/hashtag.service.mjs +1 -1
  53. package/esm2022/lib/services/models/layer.service.mjs +1 -1
  54. package/esm2022/lib/services/models/measurement.service.mjs +1 -1
  55. package/esm2022/lib/services/models/mission.service.mjs +1 -1
  56. package/esm2022/lib/services/models/navigation.service.mjs +1 -1
  57. package/esm2022/lib/services/models/node.service.mjs +1 -1
  58. package/esm2022/lib/services/models/object3D.service.mjs +1 -1
  59. package/esm2022/lib/services/models/operation.service.mjs +1 -1
  60. package/esm2022/lib/services/models/organisation.service.mjs +1 -1
  61. package/esm2022/lib/services/models/plan.service.mjs +1 -1
  62. package/esm2022/lib/services/models/poi.service.mjs +62 -50
  63. package/esm2022/lib/services/models/profile.service.mjs +1 -1
  64. package/esm2022/lib/services/models/property.service.mjs +1 -1
  65. package/esm2022/lib/services/models/space.service.mjs +1 -1
  66. package/esm2022/lib/services/models/template.service.mjs +1 -1
  67. package/esm2022/lib/services/models/ticket.service.mjs +1 -1
  68. package/esm2022/lib/services/models/visit.service.mjs +1 -1
  69. package/esm2022/lib/services/models/zone.service.mjs +9 -5
  70. package/esm2022/lib/services/navigator.service.mjs +1 -1
  71. package/esm2022/lib/services/s3.service.mjs +1 -1
  72. package/esm2022/lib/services/search.service.mjs +1 -1
  73. package/esm2022/lib/services/support.service.mjs +1 -1
  74. package/esm2022/lib/services/tag.service.mjs +16 -16
  75. package/esm2022/lib/services/threejs-import.service.mjs +1 -1
  76. package/esm2022/lib/services/threejs.service.mjs +1 -1
  77. package/esm2022/lib/services/user.service.mjs +1 -1
  78. package/esm2022/lib/services/utils/matterport-transform.util.mjs +1 -1
  79. package/esm2022/lib/services/validators.service.mjs +1 -1
  80. package/esm2022/lib/services/viewer.service.mjs +1 -1
  81. package/esm2022/lib/services/zone-drawer.service.mjs +1 -1
  82. package/esm2022/lib/services/zoneChange.service.mjs +1 -1
  83. package/esm2022/lib/tokens/matterport-sdk-key.token.mjs +1 -1
  84. package/esm2022/lib/types.service.mjs +1 -1
  85. package/esm2022/lib/validators/email.directive.mjs +1 -1
  86. package/esm2022/lib/validators/no-empty.directive.mjs +1 -1
  87. package/esm2022/lib/validators/number.directive.mjs +1 -1
  88. package/esm2022/lib/validators/text.directive.mjs +1 -1
  89. package/esm2022/public-api.mjs +1 -1
  90. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +814 -193
  91. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
  92. package/lib/mattertagData.d.ts +4 -0
  93. package/lib/services/matterport-navigation.service.d.ts +9 -0
  94. package/lib/services/matterport-tag-copy.service.d.ts +128 -2
  95. package/lib/services/matterport-tag.service.d.ts +24 -1
  96. package/lib/services/matterport.service.d.ts +12 -3
  97. package/lib/services/models/poi.service.d.ts +1 -0
  98. package/package.json +1 -1
  99. package/src/assets/vs-app.json +67 -67
@@ -87,11 +87,11 @@ class CsvExportComponent {
87
87
  link.remove();
88
88
  }
89
89
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CsvExportComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
90
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: CsvExportComponent, selector: "lib-csv-export", inputs: { headers: "headers", rowsInput: "rowsInput", getRows: "getRows", title: "title", buttonLabel: "buttonLabel" }, ngImport: i0, template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\n {{ buttonLabel | translate}}\n</span>\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
90
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: CsvExportComponent, selector: "lib-csv-export", inputs: { headers: "headers", rowsInput: "rowsInput", getRows: "getRows", title: "title", buttonLabel: "buttonLabel" }, ngImport: i0, template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\r\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\r\n {{ buttonLabel | translate}}\r\n</span>\r\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
91
91
  }
92
92
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CsvExportComponent, decorators: [{
93
93
  type: Component,
94
- args: [{ selector: 'lib-csv-export', template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\n {{ buttonLabel | translate}}\n</span>\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"] }]
94
+ args: [{ selector: 'lib-csv-export', template: "<span [style.cursor]=\"'pointer'\" (click)=\"onClickDownload()\" class=\"download-csv d-flex\">\r\n <span class=\"iconify\" data-icon=\"mdi:file-download-outline\"></span>\r\n {{ buttonLabel | translate}}\r\n</span>\r\n", styles: [".download-csv{color:var(--smarterplan-primary)}\n"] }]
95
95
  }], propDecorators: { headers: [{
96
96
  type: Input
97
97
  }], rowsInput: [{
@@ -795,30 +795,30 @@ class SecurityCamera extends SceneComponent {
795
795
  }
796
796
  }
797
797
  function vertexShader$1() {
798
- return `
799
- uniform mat4 lightMatrix;
800
-
801
- varying vec4 vLightCoords;
802
-
803
- void main() {
804
- // create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
805
- vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
806
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
807
- }
798
+ return `
799
+ uniform mat4 lightMatrix;
800
+
801
+ varying vec4 vLightCoords;
802
+
803
+ void main() {
804
+ // create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
805
+ vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
806
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
807
+ }
808
808
  `;
809
809
  }
810
810
  function fragmentShader$1() {
811
- return `
812
- uniform vec3 color;
813
-
814
- varying vec4 vLightCoords;
815
-
816
- void main() {
817
- vec2 lightUV = vLightCoords.xy / vLightCoords.w;
818
- float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
819
-
820
- gl_FragColor = vec4(color, min(inView, 0.2));
821
- }
811
+ return `
812
+ uniform vec3 color;
813
+
814
+ varying vec4 vLightCoords;
815
+
816
+ void main() {
817
+ vec2 lightUV = vLightCoords.xy / vLightCoords.w;
818
+ float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
819
+
820
+ gl_FragColor = vec4(color, min(inView, 0.2));
821
+ }
822
822
  `;
823
823
  }
824
824
  function updateHighlightUniforms$1(projector, uniforms) {
@@ -1011,30 +1011,30 @@ class ViewFrustumMesh extends SceneComponent {
1011
1011
  }
1012
1012
  }
1013
1013
  function vertexShader() {
1014
- return `
1015
- uniform mat4 lightMatrix;
1016
-
1017
- varying vec4 vLightCoords;
1018
-
1019
- void main() {
1020
- // create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
1021
- vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
1022
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
1023
- }
1014
+ return `
1015
+ uniform mat4 lightMatrix;
1016
+
1017
+ varying vec4 vLightCoords;
1018
+
1019
+ void main() {
1020
+ // create a UV region [0,1] x [0,1] to represent what the lightMatrix "sees"
1021
+ vLightCoords = lightMatrix * modelMatrix * vec4(position, 1.0);
1022
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
1023
+ }
1024
1024
  `;
1025
1025
  }
1026
1026
  function fragmentShader() {
1027
- return `
1028
- uniform vec3 color;
1029
-
1030
- varying vec4 vLightCoords;
1031
-
1032
- void main() {
1033
- vec2 lightUV = vLightCoords.xy / vLightCoords.w;
1034
- float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
1035
-
1036
- gl_FragColor = vec4(color, min(inView, 0.2));
1037
- }
1027
+ return `
1028
+ uniform vec3 color;
1029
+
1030
+ varying vec4 vLightCoords;
1031
+
1032
+ void main() {
1033
+ vec2 lightUV = vLightCoords.xy / vLightCoords.w;
1034
+ float inView = float(max(lightUV.x, lightUV.y) <= 1.0 && min(lightUV.x, lightUV.y) >= 0.0 && vLightCoords.z > 0.0);
1035
+
1036
+ gl_FragColor = vec4(color, min(inView, 0.2));
1037
+ }
1038
1038
  `;
1039
1039
  }
1040
1040
  function updateHighlightUniforms(projector, uniforms) {
@@ -2197,66 +2197,78 @@ class PoiService {
2197
2197
  if (!sweeps || sweeps.length === 0) {
2198
2198
  return [];
2199
2199
  }
2200
- const queryStatement = `
2201
- query ListPOISLight($filter: ModelPOIFilterInput, $limit: Int, $nextToken: String) {
2202
- listPOIS(filter: $filter, limit: $limit, nextToken: $nextToken) {
2203
- items {
2204
- id
2205
- coordinate
2206
- specific
2207
- private
2208
- global
2209
- type
2210
- elementID
2211
- level
2212
- role
2213
- category
2214
- domainID
2215
- metadata
2216
- tagIcon
2217
- tagDescription
2218
- tagImageID
2219
- matterportSweepID
2220
- createdAt
2221
- updatedAt
2222
- }
2223
- nextToken
2224
- }
2225
- }
2200
+ const queryStatement = `
2201
+ query ListPOISLight($filter: ModelPOIFilterInput, $limit: Int, $nextToken: String) {
2202
+ listPOIS(filter: $filter, limit: $limit, nextToken: $nextToken) {
2203
+ items {
2204
+ id
2205
+ coordinate
2206
+ specific
2207
+ private
2208
+ global
2209
+ type
2210
+ elementID
2211
+ level
2212
+ role
2213
+ category
2214
+ domainID
2215
+ metadata
2216
+ tagIcon
2217
+ tagDescription
2218
+ tagImageID
2219
+ matterportSweepID
2220
+ createdAt
2221
+ updatedAt
2222
+ }
2223
+ nextToken
2224
+ }
2225
+ }
2226
2226
  `;
2227
2227
  const CHUNK_SIZE = 50; // safe margin for an "or" filter expression's practical size
2228
2228
  const chunks = [];
2229
2229
  for (let i = 0; i < sweeps.length; i += CHUNK_SIZE) {
2230
2230
  chunks.push(sweeps.slice(i, i + CHUNK_SIZE));
2231
2231
  }
2232
- let allPois = [];
2233
- for (const chunk of chunks) {
2234
- let nextToken = null;
2235
- do {
2236
- const variables = {
2237
- filter: {
2238
- or: chunk.map((sweep) => ({ matterportSweepID: { eq: sweep } }))
2239
- },
2240
- limit: 1000,
2241
- nextToken
2242
- };
2232
+ // Each chunk is a full table Scan of its own: running them in parallel instead of one after
2233
+ // the other divides the wait by the number of chunks (7.6 s -> ~1 s on a 456-sweep space).
2234
+ // Results are flattened in chunk order, so the output is the same as the sequential version.
2235
+ const results = await Promise.all(chunks.map(chunk => this.scanPoisForSweepChunk(queryStatement, chunk)));
2236
+ return results.flat();
2237
+ }
2238
+ async scanPoisForSweepChunk(queryStatement, chunk) {
2239
+ const pois = [];
2240
+ let nextToken = null;
2241
+ do {
2242
+ const variables = {
2243
+ filter: {
2244
+ or: chunk.map((sweep) => ({ matterportSweepID: { eq: sweep } }))
2245
+ },
2246
+ limit: 1000,
2247
+ nextToken
2248
+ };
2249
+ // A failed page is retried once: giving up at once silently dropped the rest of the
2250
+ // chunk (up to 50 sweeps worth of POIs) from the copy or recalibration lists.
2251
+ let resData = null;
2252
+ for (let attempt = 1; attempt <= 2 && !resData; attempt++) {
2243
2253
  try {
2244
2254
  const response = (await API.graphql(graphqlOperation(queryStatement, variables)));
2245
- const resData = response.data.listPOIS;
2246
- if (resData && resData.items.length > 0) {
2247
- allPois = [...allPois, ...resData.items];
2248
- }
2249
- nextToken = resData?.nextToken ?? null;
2255
+ resData = response.data.listPOIS ?? { items: [], nextToken: null };
2250
2256
  }
2251
2257
  catch (error) {
2252
- // A failed page shouldn't abort the whole batch — log and stop paging this chunk only,
2253
- // the rest of the copy process continues with whatever was already collected.
2254
- console.error(`Error while fetching POIs for a sweep chunk:`, error);
2255
- nextToken = null;
2258
+ // Amplify rejects when one item errors but still hands over the readable ones
2259
+ if (error?.data?.listPOIS) {
2260
+ resData = error.data.listPOIS;
2261
+ }
2262
+ else if (attempt === 2) {
2263
+ // the rest of the process continues with whatever was already collected
2264
+ console.error(`Error while fetching POIs for a sweep chunk:`, error);
2265
+ }
2256
2266
  }
2257
- } while (nextToken);
2258
- }
2259
- return allPois;
2267
+ }
2268
+ pois.push(...(resData?.items || []).filter((poi) => !!poi));
2269
+ nextToken = resData?.nextToken ?? null;
2270
+ } while (nextToken);
2271
+ return pois;
2260
2272
  }
2261
2273
  unsubscribe() {
2262
2274
  this.destroy$.next(true);
@@ -2345,6 +2357,15 @@ class MattertagData {
2345
2357
  color: { r: 1, g: 1, b: 1 },
2346
2358
  };
2347
2359
  }
2360
+ /**
2361
+ * Safely retrieves the display name depending on object properties (name, title)
2362
+ */
2363
+ getDisplayName() {
2364
+ if (!this.object) {
2365
+ return '';
2366
+ }
2367
+ return this.object.name || this.object.title || '';
2368
+ }
2348
2369
  /**
2349
2370
  * Get the descriptor for sdk.Tag.add(...descriptor)
2350
2371
  * Format : [{ label, description, anchorPosition, stemVector, color, id? }]
@@ -2551,6 +2572,8 @@ class MatterportNavigationService {
2551
2572
  currentRooms = [];
2552
2573
  /** Sequence number of the floor currently visible in sdk.Floor.current (null = single-floor or unknown). */
2553
2574
  currentFloorSequence = null;
2575
+ /** True while a floorplan transition was explicitly requested through action_toolbox_floorplan. */
2576
+ floorplanRequested = false;
2554
2577
  // ── Bidirectional sid ↔ uuid lookup maps ──
2555
2578
  // sid = short runtime key used by Matterport SDK (collection keys, Sweep.moveTo, Camera.pose.sweep)
2556
2579
  // uuid = persistent identifier stored in the database (zone.sweepIDs, poi.matterportSweepID, startSweepID)
@@ -2617,12 +2640,27 @@ class MatterportNavigationService {
2617
2640
  console.log('viewer is in transition, cannot go floorplan');
2618
2641
  return;
2619
2642
  }
2643
+ this.floorplanRequested = true;
2620
2644
  try {
2621
2645
  await sdk.Mode.moveTo('mode.floorplan');
2622
2646
  }
2623
2647
  catch (e) {
2624
2648
  console.log('cannot go to floorplan', e);
2625
2649
  }
2650
+ finally {
2651
+ this.floorplanRequested = false;
2652
+ }
2653
+ }
2654
+ /**
2655
+ * When a dollhouse camera looks straight down, Matterport reports 'mode.floorplan'
2656
+ * (its "peekaboo" corrected mode) although the camera is still in dollhouse.
2657
+ * The native mode buttons are hidden, so such a switch that we did not request
2658
+ * is never a user action and must not be treated as a real mode change.
2659
+ */
2660
+ isUnrequestedFloorplanSwitch(previousMode, newMode) {
2661
+ return (previousMode === 'mode.dollhouse' &&
2662
+ newMode === 'mode.floorplan' &&
2663
+ !this.floorplanRequested);
2626
2664
  }
2627
2665
  action_toolbox_inside_view(sdk) {
2628
2666
  sdk.Mode.moveTo('mode.inside');
@@ -2989,11 +3027,11 @@ class MatterportPointerService {
2989
3027
  }
2990
3028
  updateCursorDisplay(poseMatterport) {
2991
3029
  if (this.textDisplayCursorPositionPanel && poseMatterport) {
2992
- this.textDisplayCursorPositionPanel.innerHTML = `position:
2993
- ${pointToString(poseMatterport.position)}\n
2994
- normal:
2995
- ${pointToString(poseMatterport.normal)}\n
2996
- floorId:
3030
+ this.textDisplayCursorPositionPanel.innerHTML = `position:
3031
+ ${pointToString(poseMatterport.position)}\n
3032
+ normal:
3033
+ ${pointToString(poseMatterport.normal)}\n
3034
+ floorId:
2997
3035
  ${poseMatterport.floorId}`;
2998
3036
  if (this.getCursorPositionButton) {
2999
3037
  this.getCursorPositionButton.style.display = 'none';
@@ -3653,6 +3691,13 @@ class MatterportTagService {
3653
3691
  tagService = null;
3654
3692
  tagsCurrentOpacity = new Map();
3655
3693
  tagsCurrentEnabled = new Map();
3694
+ /** tagId -> icon name last applied, so an unchanged icon is not re-applied. */
3695
+ tagsCurrentIcon = new Map();
3696
+ /** Ids of the global SDK Tag collection, and the tags waiting to appear in it, see waitForTagInSdk(). */
3697
+ sdkTagIds = new Set();
3698
+ sdkTagWaiters = new Map();
3699
+ sdkTagSubscription = null;
3700
+ sdkTagSubscriptionSdk = null;
3656
3701
  /**
3657
3702
  * Single layer holding every Smarterplan tag, added to every view of the space so a
3658
3703
  * tag stays in place — same sid, same content — when the user switches between the
@@ -3696,11 +3741,92 @@ class MatterportTagService {
3696
3741
  * A layer-scoped Tag module only exposes the *replayable* mutations, meant to build
3697
3742
  * the layer's own content; applying them to an already-created tag silently does
3698
3743
  * nothing — which is what left every pin white instead of carrying its icon. Only
3699
- * `add` has to go through the layer, see getTagCreationModule().
3744
+ * `add` has to go through the layer, see getTagCreationModule(). The global module
3745
+ * only knows a layer-created pin once it is rendered: see waitForTagInSdk().
3700
3746
  */
3701
3747
  getTagModule(sdk, tagId) {
3702
3748
  return sdk?.Tag;
3703
3749
  }
3750
+ /** Icon name last applied to a tag, if any. */
3751
+ getCachedTagIcon(tagId) {
3752
+ return this.tagsCurrentIcon.get(tagId);
3753
+ }
3754
+ /** Applies an icon to a tag through its own module and remembers it on success. */
3755
+ async editTagIcon(sdk, tagId, iconName) {
3756
+ await this.getTagModule(sdk, tagId).editIcon(tagId, iconName);
3757
+ this.tagsCurrentIcon.set(tagId, iconName);
3758
+ }
3759
+ /**
3760
+ * Resolves once the global SDK Tag collection — the one edits go through — contains the
3761
+ * tag, so edits do not hit a pin that is not rendered yet ("setPinRenderOverrides on a pin
3762
+ * that doesn't exist"). Gives up after `timeoutMs` and resolves anyway; falls back to a
3763
+ * short wait when the collection cannot be observed. One subscription is shared by every
3764
+ * waiting tag: a space loads hundreds of them.
3765
+ */
3766
+ waitForTagInSdk(sdk, tagId, timeoutMs = 1500) {
3767
+ if (!tagId || !this.observeSdkTags(sdk))
3768
+ return wait(100);
3769
+ if (this.sdkTagIds.has(tagId))
3770
+ return Promise.resolve();
3771
+ return new Promise((resolve) => {
3772
+ const waiters = this.sdkTagWaiters.get(tagId) || [];
3773
+ const timer = setTimeout(() => {
3774
+ const remaining = (this.sdkTagWaiters.get(tagId) || []).filter(waiter => waiter !== done);
3775
+ if (remaining.length)
3776
+ this.sdkTagWaiters.set(tagId, remaining);
3777
+ else
3778
+ this.sdkTagWaiters.delete(tagId);
3779
+ resolve();
3780
+ }, timeoutMs);
3781
+ const done = () => { clearTimeout(timer); resolve(); };
3782
+ waiters.push(done);
3783
+ this.sdkTagWaiters.set(tagId, waiters);
3784
+ });
3785
+ }
3786
+ /** Keeps sdkTagIds in sync with the global SDK Tag collection; false when it cannot be observed. */
3787
+ observeSdkTags(sdk) {
3788
+ if (this.sdkTagSubscription && this.sdkTagSubscriptionSdk === sdk)
3789
+ return true;
3790
+ const data = sdk?.Tag?.data;
3791
+ if (typeof data?.subscribe !== 'function')
3792
+ return false;
3793
+ this.stopObservingSdkTags();
3794
+ const markPresent = (id) => {
3795
+ this.sdkTagIds.add(id);
3796
+ const waiters = this.sdkTagWaiters.get(id);
3797
+ if (waiters) {
3798
+ this.sdkTagWaiters.delete(id);
3799
+ waiters.forEach(waiter => waiter());
3800
+ }
3801
+ };
3802
+ try {
3803
+ this.sdkTagSubscriptionSdk = sdk;
3804
+ this.sdkTagSubscription = data.subscribe({
3805
+ onAdded: (id) => markPresent(id),
3806
+ onRemoved: (id) => this.sdkTagIds.delete(id),
3807
+ onCollectionUpdated: (collection) => {
3808
+ const ids = typeof collection?.keys === 'function'
3809
+ ? Array.from(collection.keys()) : Object.keys(collection || {});
3810
+ this.sdkTagIds = new Set(ids);
3811
+ ids.forEach(id => this.sdkTagWaiters.has(id) && markPresent(id));
3812
+ },
3813
+ });
3814
+ return true;
3815
+ }
3816
+ catch {
3817
+ this.sdkTagSubscriptionSdk = null;
3818
+ return false;
3819
+ }
3820
+ }
3821
+ stopObservingSdkTags() {
3822
+ try {
3823
+ this.sdkTagSubscription?.cancel?.();
3824
+ }
3825
+ catch { }
3826
+ this.sdkTagSubscription = null;
3827
+ this.sdkTagSubscriptionSdk = null;
3828
+ this.sdkTagIds.clear();
3829
+ }
3704
3830
  /**
3705
3831
  * Returns the Tag module every new tag must be created through: the shared layer
3706
3832
  * when the View API is available, the global sdk.Tag otherwise.
@@ -3855,7 +3981,7 @@ class MatterportTagService {
3855
3981
  const [createdSid] = await tagModule.add(...descriptor);
3856
3982
  this.mattertagIDs.push(createdSid);
3857
3983
  this.dictionnaryTags.set(createdSid, mattertagData);
3858
- await wait(100);
3984
+ await this.waitForTagInSdk(sdk, createdSid);
3859
3985
  await setTagIconAndOpacity(createdSid, mattertagData);
3860
3986
  injectHtmlInTag(mattertagData.getType(), mattertagData.getObject(), createdSid).catch(err => {
3861
3987
  if (isDevMode())
@@ -3976,6 +4102,7 @@ class MatterportTagService {
3976
4102
  resetVisibilityCache() {
3977
4103
  this.tagsCurrentOpacity.clear();
3978
4104
  this.tagsCurrentEnabled.clear();
4105
+ this.tagsCurrentIcon.clear();
3979
4106
  }
3980
4107
  /** Removes a specific tag from the viewer and local dictionaries. */
3981
4108
  async deleteMattertagFromId(sdk, mattertagID) {
@@ -3983,6 +4110,9 @@ class MatterportTagService {
3983
4110
  if (sdk)
3984
4111
  await this.getTagModule(sdk, mattertagID).remove(mattertagID);
3985
4112
  this.dictionnaryTags.delete(mattertagID);
4113
+ this.tagsCurrentIcon.delete(mattertagID);
4114
+ this.tagsCurrentOpacity.delete(mattertagID);
4115
+ this.tagsCurrentEnabled.delete(mattertagID);
3986
4116
  const index = this.mattertagIDs.indexOf(mattertagID);
3987
4117
  if (index > -1)
3988
4118
  this.mattertagIDs.splice(index, 1);
@@ -4044,14 +4174,8 @@ class MatterportTagService {
4044
4174
  }
4045
4175
  mattertagData.setPoi(poi);
4046
4176
  const createdTagID = await this.addMattertagToViewer(sdk, mattertagData);
4047
- console.log('---------------');
4048
- console.log('element id ' + mattertagData.elementID);
4049
- console.log('position: ', mattertagData.getPosition());
4050
- console.log('rotation: ', mattertagData.getRotation());
4051
- console.log('sweep id: ', mattertagData.getSweepID());
4052
- console.log('---------------');
4053
4177
  if (createdTagID && sdk) {
4054
- await wait(100);
4178
+ await this.waitForTagInSdk(sdk, createdTagID);
4055
4179
  await setTagIconAndOpacity(createdTagID, mattertagData);
4056
4180
  injectHtmlInTag(tagType, object, createdTagID).catch(err => {
4057
4181
  if (isDevMode())
@@ -4080,6 +4204,7 @@ class MatterportTagService {
4080
4204
  const tagModule = this.getTagModule(sdk, mattertagID);
4081
4205
  await sdk.Asset.registerTexture(iconName, iconPath);
4082
4206
  await tagModule.editIcon(mattertagID, iconName);
4207
+ this.tagsCurrentIcon.set(mattertagID, iconName);
4083
4208
  if (applyOpacity) {
4084
4209
  await tagModule.editOpacity(mattertagID, opacity);
4085
4210
  }
@@ -4209,6 +4334,8 @@ class MatterportTagService {
4209
4334
  this.dictionnaryTags.clear();
4210
4335
  this.tagsCurrentOpacity.clear();
4211
4336
  this.tagsCurrentEnabled.clear();
4337
+ this.tagsCurrentIcon.clear();
4338
+ this.stopObservingSdkTags();
4212
4339
  this.tagAddQueue = Promise.resolve();
4213
4340
  this.mattertagToFollow = null;
4214
4341
  this.mattertagIDs = [];
@@ -4332,6 +4459,12 @@ class MatterportService {
4332
4459
  tagAction = new Subject();
4333
4460
  /** Views (furnished, defurnished...) exposed by the SDK for the current space. */
4334
4461
  viewsByType = new Map();
4462
+ /** Settles once every static icon texture has been registered (or failed). */
4463
+ iconsRegistered = Promise.resolve();
4464
+ /** Names of the textures successfully registered in the current SDK session. */
4465
+ registeredIcons = new Set();
4466
+ /** tagId -> signed source last registered as a custom texture, and its texture name. */
4467
+ customIconCache = new Map();
4335
4468
  currentView = null;
4336
4469
  viewSubscriptions = [];
4337
4470
  /** Emits the active view every time the user switches between furnished/defurnished. */
@@ -4460,13 +4593,16 @@ class MatterportService {
4460
4593
  this.addCustomCss();
4461
4594
  this.pointerService.startAdminCursorTracker(this.sdk, this.container, () => this.poseMatterport, () => this.poseCamera);
4462
4595
  // Load Mattertag icons and custom subscriptions
4596
+ this.registeredIcons.clear();
4597
+ this.customIconCache.clear();
4598
+ const iconRegistrations = [];
4463
4599
  switch (module) {
4464
4600
  case SpModule.IMMO:
4465
- this.sdk.Asset.registerTexture('icon-ticket', this.config.my_config.icon_ticket);
4466
- this.sdk.Asset.registerTexture('icon-equipment', this.config.my_config.icon_equipment);
4467
- this.sdk.Asset.registerTexture('icon-measure', this.config.my_config.icon_measure);
4468
- this.sdk.Asset.registerTexture('icon-data', this.config.my_config.icon_data);
4469
- this.sdk.Asset.registerTexture('icon-object3d', this.config.my_config.icon_object3d);
4601
+ iconRegistrations.push(this.registerIcon('icon-ticket', this.config.my_config.icon_ticket));
4602
+ iconRegistrations.push(this.registerIcon('icon-equipment', this.config.my_config.icon_equipment));
4603
+ iconRegistrations.push(this.registerIcon('icon-measure', this.config.my_config.icon_measure));
4604
+ iconRegistrations.push(this.registerIcon('icon-data', this.config.my_config.icon_data));
4605
+ iconRegistrations.push(this.registerIcon('icon-object3d', this.config.my_config.icon_object3d));
4470
4606
  this.sdk.Measurements.data.subscribe({
4471
4607
  onAdded: function (index, item, collection) {
4472
4608
  this.measurementService.setLastMeasure(item.points);
@@ -4483,20 +4619,19 @@ class MatterportService {
4483
4619
  }.bind(this));
4484
4620
  break;
4485
4621
  case SpModule.MUSEUM:
4486
- this.sdk.Asset.registerTexture('icon-data', this.config.my_config.icon_data);
4622
+ iconRegistrations.push(this.registerIcon('icon-data', this.config.my_config.icon_data));
4487
4623
  break;
4488
4624
  case SpModule.HOTEL:
4489
- this.sdk.Asset.registerTexture('icon_room_available', this.config.my_config.icon_room_available);
4490
- this.sdk.Asset.registerTexture('icon_room_unavailable', this.config.my_config.icon_room_unavailable);
4491
- this.sdk.Asset.registerTexture('icon_room_chosen', this.config.my_config.icon_room_chosen);
4492
- this.sdk.Asset.registerTexture('icon_room_viewed', this.config.my_config.icon_room_viewed);
4625
+ iconRegistrations.push(this.registerIcon('icon_room_available', this.config.my_config.icon_room_available));
4626
+ iconRegistrations.push(this.registerIcon('icon_room_unavailable', this.config.my_config.icon_room_unavailable));
4627
+ iconRegistrations.push(this.registerIcon('icon_room_chosen', this.config.my_config.icon_room_chosen));
4628
+ iconRegistrations.push(this.registerIcon('icon_room_viewed', this.config.my_config.icon_room_viewed));
4493
4629
  break;
4494
4630
  default:
4495
4631
  break;
4496
4632
  }
4497
- this.sdk.Asset.registerTexture('icon-position', this.config.my_config.icon_position)
4498
- .catch((e) => { if (isDevMode())
4499
- console.warn('icon-position texture registration failed', e); });
4633
+ iconRegistrations.push(this.registerIcon('icon-position', this.config.my_config.icon_position));
4634
+ this.iconsRegistered = Promise.allSettled(iconRegistrations).then(() => undefined);
4500
4635
  // Current Room (used for getting bounding box and eventually lazy load mattertag or object3D inside current Room)
4501
4636
  this.sdk.Room.current.subscribe((currentRoom) => {
4502
4637
  const rooms = Array.isArray(currentRoom) ? currentRoom : (currentRoom ? [currentRoom] : []);
@@ -4527,6 +4662,10 @@ class MatterportService {
4527
4662
  }.bind(this));
4528
4663
  //Camera mode
4529
4664
  this.sdk.Mode.current.subscribe((mode) => {
4665
+ if (this.navigationService.isUnrequestedFloorplanSwitch(this.cameraMode, mode)) {
4666
+ // Top-down dollhouse camera reported as floorplan: still dollhouse, ignore it
4667
+ return;
4668
+ }
4530
4669
  if (this.cameraMode !== mode) {
4531
4670
  this.navigationService.setCameraMode(mode);
4532
4671
  // Refresh tag visibility on every mode change (INSIDE vs DOLLHOUSE vs FLOORPLAN)
@@ -4854,15 +4993,29 @@ class MatterportService {
4854
4993
  /** Applies a pre-registered icon to a Mattertag. */
4855
4994
  async setRegistredIconForTag(mattertagID, iconName) {
4856
4995
  try {
4857
- this.matterportTagService.getTagModule(this.sdk, mattertagID).editIcon(mattertagID, iconName);
4996
+ await this.matterportTagService.editTagIcon(this.sdk, mattertagID, iconName);
4858
4997
  }
4859
4998
  catch (e) {
4860
4999
  if (isDevMode())
4861
5000
  console.warn('Could not edit icon', iconName, e);
4862
5001
  }
4863
5002
  }
4864
- /** Sets the appropriate icon and opacity for a tag based on its type and POI metadata. */
5003
+ /** Registers a static texture and remembers its name once the SDK accepted it. */
5004
+ async registerIcon(iconName, iconPath) {
5005
+ if (!iconPath)
5006
+ return;
5007
+ try {
5008
+ await this.sdk.Asset.registerTexture(iconName, iconPath);
5009
+ this.registeredIcons.add(iconName);
5010
+ }
5011
+ catch (e) {
5012
+ if (isDevMode())
5013
+ console.warn(`[MatterportService] ${iconName} texture registration failed`, e);
5014
+ }
5015
+ }
5016
+ /** Sets the appropriate icon for a tag based on its type and POI metadata. */
4865
5017
  async setTagIconAndOpacity(mattertagID, mattertagData) {
5018
+ await this.iconsRegistered;
4866
5019
  if (this.SPModule === SpModule.HOTEL) {
4867
5020
  const room = mattertagData.getObject();
4868
5021
  let iconName = `icon_room`;
@@ -4876,7 +5029,6 @@ class MatterportService {
4876
5029
  return;
4877
5030
  }
4878
5031
  const stringTagType = poiTypeToString(mattertagData.getType());
4879
- let opacity = this.config.my_config.DEFAULT_OPACITY_TAG;
4880
5032
  let iconName = `icon-${stringTagType}`;
4881
5033
  const poi = mattertagData.getPoi();
4882
5034
  if (poi && poi.tagIcon) {
@@ -4888,31 +5040,45 @@ class MatterportService {
4888
5040
  }
4889
5041
  }
4890
5042
  if (tagIcon.src) {
4891
- const pathSigned = await this.tagService.getSignedTagIconSource(tagIcon.src);
4892
- if (pathSigned) {
4893
- try {
4894
- iconName = `icon-${stringTagType}-${mattertagID}-${mattertagData.customIconIndex}`;
4895
- await this.sdk.Asset.registerTexture(iconName, pathSigned);
4896
- mattertagData.customIconIndex += 1;
4897
- }
4898
- catch {
4899
- console.log('error registerIcon');
5043
+ // keyed on the stored source, not on the signed URL: a new signature every call
5044
+ const cached = this.customIconCache.get(mattertagID);
5045
+ if (cached && cached.src === tagIcon.src) {
5046
+ iconName = cached.iconName;
5047
+ }
5048
+ else {
5049
+ const pathSigned = await this.tagService.getSignedTagIconSource(tagIcon.src);
5050
+ if (pathSigned) {
5051
+ try {
5052
+ const customIconName = `icon-${stringTagType}-${mattertagID}-${mattertagData.customIconIndex}`;
5053
+ await this.sdk.Asset.registerTexture(customIconName, pathSigned);
5054
+ mattertagData.customIconIndex += 1;
5055
+ this.customIconCache.set(mattertagID, { src: tagIcon.src, iconName: customIconName });
5056
+ this.registeredIcons.add(customIconName);
5057
+ iconName = customIconName;
5058
+ }
5059
+ catch {
5060
+ console.log('error registerIcon');
5061
+ }
4900
5062
  }
4901
5063
  }
4902
5064
  }
4903
- if (tagIcon.opacity) {
4904
- opacity = tagIcon.opacity;
4905
- }
4906
5065
  }
4907
5066
  await this.editTagIcon(mattertagID, iconName);
4908
5067
  }
4909
5068
  /**
4910
- * Applies an icon to a tag. The call is awaited and its rejection reported: a
4911
- * silently dropped promise here leaves the pin with its default white look.
5069
+ * Applies an icon to a tag, unless that icon is already the one applied. The call is
5070
+ * awaited and its rejection reported: a silently dropped promise here leaves the pin
5071
+ * with its default white look.
4912
5072
  */
4913
5073
  async editTagIcon(mattertagID, iconName) {
5074
+ if (this.matterportTagService.getCachedTagIcon(mattertagID) === iconName)
5075
+ return;
5076
+ // A texture that was never registered (no icon configured for this type) can only fail:
5077
+ // the pin keeps its default look, as before, without a doomed SDK call.
5078
+ if (!this.registeredIcons.has(iconName))
5079
+ return;
4914
5080
  try {
4915
- await this.matterportTagService.getTagModule(this.sdk, mattertagID).editIcon(mattertagID, iconName);
5081
+ await this.matterportTagService.editTagIcon(this.sdk, mattertagID, iconName);
4916
5082
  }
4917
5083
  catch (error) {
4918
5084
  if (isDevMode())
@@ -5015,22 +5181,22 @@ class MatterportService {
5015
5181
  );
5016
5182
  }*/
5017
5183
  addCustomCss() {
5018
- const css = `
5019
- .footer-ui,
5020
- .showcase-footer,
5021
- .mp-nova-btn-group,
5022
- .footer-divider,
5023
- .logo-link,
5024
- .footer-logo,
5025
- .bottom-ui,
5026
- img.logo-image,
5027
- img[class^="logo-image"],
5028
- img[class*=" logo-image"],
5029
- [data-testid="showcase-footer"] {
5030
- opacity: 0 !important;
5031
- visibility: hidden !important;
5032
- pointer-events: none !important;
5033
- }
5184
+ const css = `
5185
+ .footer-ui,
5186
+ .showcase-footer,
5187
+ .mp-nova-btn-group,
5188
+ .footer-divider,
5189
+ .logo-link,
5190
+ .footer-logo,
5191
+ .bottom-ui,
5192
+ img.logo-image,
5193
+ img[class^="logo-image"],
5194
+ img[class*=" logo-image"],
5195
+ [data-testid="showcase-footer"] {
5196
+ opacity: 0 !important;
5197
+ visibility: hidden !important;
5198
+ pointer-events: none !important;
5199
+ }
5034
5200
  `;
5035
5201
  // 1st method: Via Scene SDK
5036
5202
  if (this.sdk?.Scene?.addStyle) {
@@ -8046,11 +8212,11 @@ class ModalSwitchVisitComponent {
8046
8212
  });
8047
8213
  }
8048
8214
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ModalSwitchVisitComponent, deps: [{ token: i1$2.NgbActiveModal }, { token: i1$1.Router }, { token: ViewerService }], target: i0.ɵɵFactoryTarget.Component });
8049
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ModalSwitchVisitComponent, selector: "lib-modal-switch-visit", inputs: { content: "content", model3D: "model3D", spaceID: "spaceID", dataToMove: "dataToMove" }, ngImport: i0, template: "<div class=\"modal-container\">\n <p>{{content}}</p>\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\n</div>\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
8215
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ModalSwitchVisitComponent, selector: "lib-modal-switch-visit", inputs: { content: "content", model3D: "model3D", spaceID: "spaceID", dataToMove: "dataToMove" }, ngImport: i0, template: "<div class=\"modal-container\">\r\n <p>{{content}}</p>\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\r\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\r\n</div>\r\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
8050
8216
  }
8051
8217
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ModalSwitchVisitComponent, decorators: [{
8052
8218
  type: Component,
8053
- args: [{ selector: 'lib-modal-switch-visit', template: "<div class=\"modal-container\">\n <p>{{content}}</p>\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\n</div>\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"] }]
8219
+ args: [{ selector: 'lib-modal-switch-visit', template: "<div class=\"modal-container\">\r\n <p>{{content}}</p>\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"switchVisit()\">{{'Yes, switch visit' | translate }}</button>\r\n <button class=\"btn btn-outline-primary rounded-pill\"(click)=\"activeModal.close()\">{{'No, stay here' | translate }}</button>\r\n</div>\r\n", styles: [".modal-container{margin:10px}button{margin-right:8px}\n"] }]
8054
8220
  }], ctorParameters: () => [{ type: i1$2.NgbActiveModal }, { type: i1$1.Router }, { type: ViewerService }], propDecorators: { content: [{
8055
8221
  type: Input
8056
8222
  }], model3D: [{
@@ -8587,14 +8753,18 @@ class ZoneService {
8587
8753
  async getZonesBySpace(spaceID) {
8588
8754
  const list = [];
8589
8755
  if (this.API.__proto__.hasOwnProperty('ZonesBySpace')) {
8590
- const zones = await this.API.__proto__.ZonesBySpace(spaceID);
8591
- if (zones) {
8592
- for (const zone of zones.items) {
8756
+ // Paginated: without a nextToken loop, only the first page (100 zones) was returned and
8757
+ // the sweeps of the other zones silently disappeared.
8758
+ let nextToken = null;
8759
+ do {
8760
+ const zones = await this.API.__proto__.ZonesBySpace(spaceID, null, null, 1000, nextToken);
8761
+ for (const zone of zones?.items || []) {
8593
8762
  const zoneObject = { ...zone };
8594
8763
  zoneObject.virtual = this.zoneIsVirtual(zone);
8595
8764
  list.push(zoneObject);
8596
8765
  }
8597
- }
8766
+ nextToken = zones?.nextToken ?? null;
8767
+ } while (nextToken);
8598
8768
  return list;
8599
8769
  }
8600
8770
  else {
@@ -13064,11 +13234,11 @@ class SearchBarComponent {
13064
13234
  this.searchEvent.emit(results);
13065
13235
  }
13066
13236
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SearchBarComponent, deps: [{ token: SearchService }], target: i0.ɵɵFactoryTarget.Component });
13067
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SearchBarComponent, selector: "lib-search-bar", inputs: { searchable: "searchable", objectType: "objectType" }, outputs: { searchEvent: "searchEvent" }, ngImport: i0, template: "<div class=\"col-md-8 searchbar\">\n <div class=\"row depend\">\n <div class=\"col-md-5\">\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\n </div>\n <div class=\"col-md-5\">\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\n translate}}</button>\n </div>\n </div>\n</div>", styles: [".searchbar{margin-bottom:10px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
13237
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SearchBarComponent, selector: "lib-search-bar", inputs: { searchable: "searchable", objectType: "objectType" }, outputs: { searchEvent: "searchEvent" }, ngImport: i0, template: "<div class=\"col-md-8 searchbar\">\r\n <div class=\"row depend\">\r\n <div class=\"col-md-5\">\r\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\r\n </div>\r\n <div class=\"col-md-5\">\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n</div>", styles: [".searchbar{margin-bottom:10px}\n"], dependencies: [{ kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
13068
13238
  }
13069
13239
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SearchBarComponent, decorators: [{
13070
13240
  type: Component,
13071
- args: [{ selector: 'lib-search-bar', template: "<div class=\"col-md-8 searchbar\">\n <div class=\"row depend\">\n <div class=\"col-md-5\">\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\n </div>\n <div class=\"col-md-5\">\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\n translate}}</button>\n </div>\n </div>\n</div>", styles: [".searchbar{margin-bottom:10px}\n"] }]
13241
+ args: [{ selector: 'lib-search-bar', template: "<div class=\"col-md-8 searchbar\">\r\n <div class=\"row depend\">\r\n <div class=\"col-md-5\">\r\n <input id=\"searchbar\" type=\"text\" class=\"form-control\" placeholder=\"keyword\" />\r\n </div>\r\n <div class=\"col-md-5\">\r\n <button class=\"btn btn-outline-primary rounded-pill\" (click)=\"onSubmit()\">{{ 'Search' |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n</div>", styles: [".searchbar{margin-bottom:10px}\n"] }]
13072
13242
  }], ctorParameters: () => [{ type: SearchService }], propDecorators: { searchable: [{
13073
13243
  type: Input
13074
13244
  }], objectType: [{
@@ -13516,11 +13686,11 @@ class LoaderComponent {
13516
13686
  document.documentElement.style.setProperty('--color', this.color);
13517
13687
  }
13518
13688
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13519
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: LoaderComponent, selector: "lib-loader", inputs: { useLogo: "useLogo", color: "color" }, ngImport: i0, template: "<div class=\"sk-fading-circle\">\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\n <div class=\"sk-circle1 sk-circle\"></div>\n <div class=\"sk-circle2 sk-circle\"></div>\n <div class=\"sk-circle3 sk-circle\"></div>\n <div class=\"sk-circle4 sk-circle\"></div>\n <div class=\"sk-circle5 sk-circle\"></div>\n <div class=\"sk-circle6 sk-circle\"></div>\n <div class=\"sk-circle7 sk-circle\"></div>\n <div class=\"sk-circle8 sk-circle\"></div>\n <div class=\"sk-circle9 sk-circle\"></div>\n <div class=\"sk-circle10 sk-circle\"></div>\n <div class=\"sk-circle11 sk-circle\"></div>\n <div class=\"sk-circle12 sk-circle\"></div>\n</div>\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"], dependencies: [{ kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
13689
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: LoaderComponent, selector: "lib-loader", inputs: { useLogo: "useLogo", color: "color" }, ngImport: i0, template: "<div class=\"sk-fading-circle\">\r\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\r\n <div class=\"sk-circle1 sk-circle\"></div>\r\n <div class=\"sk-circle2 sk-circle\"></div>\r\n <div class=\"sk-circle3 sk-circle\"></div>\r\n <div class=\"sk-circle4 sk-circle\"></div>\r\n <div class=\"sk-circle5 sk-circle\"></div>\r\n <div class=\"sk-circle6 sk-circle\"></div>\r\n <div class=\"sk-circle7 sk-circle\"></div>\r\n <div class=\"sk-circle8 sk-circle\"></div>\r\n <div class=\"sk-circle9 sk-circle\"></div>\r\n <div class=\"sk-circle10 sk-circle\"></div>\r\n <div class=\"sk-circle11 sk-circle\"></div>\r\n <div class=\"sk-circle12 sk-circle\"></div>\r\n</div>\r\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"], dependencies: [{ kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
13520
13690
  }
13521
13691
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LoaderComponent, decorators: [{
13522
13692
  type: Component,
13523
- args: [{ selector: 'lib-loader', template: "<div class=\"sk-fading-circle\">\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\n <div class=\"sk-circle1 sk-circle\"></div>\n <div class=\"sk-circle2 sk-circle\"></div>\n <div class=\"sk-circle3 sk-circle\"></div>\n <div class=\"sk-circle4 sk-circle\"></div>\n <div class=\"sk-circle5 sk-circle\"></div>\n <div class=\"sk-circle6 sk-circle\"></div>\n <div class=\"sk-circle7 sk-circle\"></div>\n <div class=\"sk-circle8 sk-circle\"></div>\n <div class=\"sk-circle9 sk-circle\"></div>\n <div class=\"sk-circle10 sk-circle\"></div>\n <div class=\"sk-circle11 sk-circle\"></div>\n <div class=\"sk-circle12 sk-circle\"></div>\n</div>\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"] }]
13693
+ args: [{ selector: 'lib-loader', template: "<div class=\"sk-fading-circle\">\r\n <img *ngIf=\"useLogo\" src=\"../../../../assets/icons/smarterplan.svg\">\r\n <div class=\"sk-circle1 sk-circle\"></div>\r\n <div class=\"sk-circle2 sk-circle\"></div>\r\n <div class=\"sk-circle3 sk-circle\"></div>\r\n <div class=\"sk-circle4 sk-circle\"></div>\r\n <div class=\"sk-circle5 sk-circle\"></div>\r\n <div class=\"sk-circle6 sk-circle\"></div>\r\n <div class=\"sk-circle7 sk-circle\"></div>\r\n <div class=\"sk-circle8 sk-circle\"></div>\r\n <div class=\"sk-circle9 sk-circle\"></div>\r\n <div class=\"sk-circle10 sk-circle\"></div>\r\n <div class=\"sk-circle11 sk-circle\"></div>\r\n <div class=\"sk-circle12 sk-circle\"></div>\r\n</div>\r\n", styles: [".sk-fading-circle{width:88px;height:88px;position:relative}.sk-fading-circle img{width:2rem;position:absolute;left:calc(50% - 16px);top:calc(50% - 16px)}.sk-fading-circle .sk-circle{width:100%;height:100%;position:absolute;left:0;top:0}.sk-fading-circle .sk-circle:before{content:\"\";display:block;margin:0 auto;width:15%;height:15%;background-color:var(--color);border-radius:100%;-webkit-animation:sk-circleFadeDelay 1.2s infinite ease-in-out both;animation:sk-circleFadeDelay 1.2s infinite ease-in-out both}.sk-fading-circle .sk-circle2{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);transform:rotate(30deg)}.sk-fading-circle .sk-circle3{-webkit-transform:rotate(60deg);-ms-transform:rotate(60deg);transform:rotate(60deg)}.sk-fading-circle .sk-circle4{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.sk-fading-circle .sk-circle5{-webkit-transform:rotate(120deg);-ms-transform:rotate(120deg);transform:rotate(120deg)}.sk-fading-circle .sk-circle6{-webkit-transform:rotate(150deg);-ms-transform:rotate(150deg);transform:rotate(150deg)}.sk-fading-circle .sk-circle7{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.sk-fading-circle .sk-circle8{-webkit-transform:rotate(210deg);-ms-transform:rotate(210deg);transform:rotate(210deg)}.sk-fading-circle .sk-circle9{-webkit-transform:rotate(240deg);-ms-transform:rotate(240deg);transform:rotate(240deg)}.sk-fading-circle .sk-circle10{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.sk-fading-circle .sk-circle11{-webkit-transform:rotate(300deg);-ms-transform:rotate(300deg);transform:rotate(300deg)}.sk-fading-circle .sk-circle12{-webkit-transform:rotate(330deg);-ms-transform:rotate(330deg);transform:rotate(330deg)}.sk-fading-circle .sk-circle2:before{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.sk-fading-circle .sk-circle3:before{-webkit-animation-delay:-1s;animation-delay:-1s}.sk-fading-circle .sk-circle4:before{-webkit-animation-delay:-.9s;animation-delay:-.9s}.sk-fading-circle .sk-circle5:before{-webkit-animation-delay:-.8s;animation-delay:-.8s}.sk-fading-circle .sk-circle6:before{-webkit-animation-delay:-.7s;animation-delay:-.7s}.sk-fading-circle .sk-circle7:before{-webkit-animation-delay:-.6s;animation-delay:-.6s}.sk-fading-circle .sk-circle8:before{-webkit-animation-delay:-.5s;animation-delay:-.5s}.sk-fading-circle .sk-circle9:before{-webkit-animation-delay:-.4s;animation-delay:-.4s}.sk-fading-circle .sk-circle10:before{-webkit-animation-delay:-.3s;animation-delay:-.3s}.sk-fading-circle .sk-circle11:before{-webkit-animation-delay:-.2s;animation-delay:-.2s}.sk-fading-circle .sk-circle12:before{-webkit-animation-delay:-.1s;animation-delay:-.1s}@-webkit-keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}@keyframes sk-circleFadeDelay{0%,39%,to{opacity:0}40%{opacity:1}}\n"] }]
13524
13694
  }], ctorParameters: () => [], propDecorators: { useLogo: [{
13525
13695
  type: Input
13526
13696
  }], color: [{
@@ -13578,11 +13748,11 @@ class SupportModalComponent {
13578
13748
  }
13579
13749
  }
13580
13750
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SupportModalComponent, deps: [{ token: SupportService }, { token: i1$2.NgbActiveModal }, { token: i3.UntypedFormBuilder }, { token: BaseUserService }, { token: ValidatorsService }], target: i0.ɵɵFactoryTarget.Component });
13581
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SupportModalComponent, selector: "app-support-modal", ngImport: i0, template: "<div class=\"modal-header\">\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\n</div>\n<div class=\"modal-body\">\n <div class=\"support-loader-container\">\n <lib-loader *ngIf=\"loading\"></lib-loader>\n </div>\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\n <div>\n <label>{{'Title' | translate}} *</label>\n <div>\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\n </div>\n </div>\n <div *ngIf=\"!currentUser\">\n <label>{{'Email' | translate}} *</label>\n <div>\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\n </div>\n </div>\n <div>\n <label>{{'Description' | translate}} *</label>\n <div>\n <textarea formControlName=\"description\" class=\"form-control\"\n data-testid=\"new-support-description\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\n </div>\n <div class=\"invalid-feedback\" >\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\n </div>\n </div>\n </form>\n\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\n </div>\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\n data-testid=\"new-support-error-info\">\n\n </div>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\n data-testid=\"new-support-close-btn\"></button>\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i11.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: LoaderComponent, selector: "lib-loader", inputs: ["useLogo", "color"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
13751
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: SupportModalComponent, selector: "app-support-modal", ngImport: i0, template: "<div class=\"modal-header\">\r\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\r\n</div>\r\n<div class=\"modal-body\">\r\n <div class=\"support-loader-container\">\r\n <lib-loader *ngIf=\"loading\"></lib-loader>\r\n </div>\r\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\r\n <div>\r\n <label>{{'Title' | translate}} *</label>\r\n <div>\r\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\r\n </div>\r\n </div>\r\n <div *ngIf=\"!currentUser\">\r\n <label>{{'Email' | translate}} *</label>\r\n <div>\r\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\r\n </div>\r\n </div>\r\n <div>\r\n <label>{{'Description' | translate}} *</label>\r\n <div>\r\n <textarea formControlName=\"description\" class=\"form-control\"\r\n data-testid=\"new-support-description\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\r\n </div>\r\n <div class=\"invalid-feedback\" >\r\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\r\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\r\n </div>\r\n </div>\r\n </form>\r\n\r\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\r\n </div>\r\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\r\n data-testid=\"new-support-error-info\">\r\n\r\n </div>\r\n</div>\r\n<div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\r\n data-testid=\"new-support-close-btn\"></button>\r\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\r\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\r\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i11.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: LoaderComponent, selector: "lib-loader", inputs: ["useLogo", "color"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
13582
13752
  }
13583
13753
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SupportModalComponent, decorators: [{
13584
13754
  type: Component,
13585
- args: [{ selector: 'app-support-modal', template: "<div class=\"modal-header\">\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\n</div>\n<div class=\"modal-body\">\n <div class=\"support-loader-container\">\n <lib-loader *ngIf=\"loading\"></lib-loader>\n </div>\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\n <div>\n <label>{{'Title' | translate}} *</label>\n <div>\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\n </div>\n </div>\n <div *ngIf=\"!currentUser\">\n <label>{{'Email' | translate}} *</label>\n <div>\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\n maxlength=\"255\">\n </div>\n <div class=\"invalid-feedback\">\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\n </div>\n </div>\n <div>\n <label>{{'Description' | translate}} *</label>\n <div>\n <textarea formControlName=\"description\" class=\"form-control\"\n data-testid=\"new-support-description\"\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\n </div>\n <div class=\"invalid-feedback\" >\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\n </div>\n </div>\n </form>\n\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\n </div>\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\n data-testid=\"new-support-error-info\">\n\n </div>\n</div>\n<div class=\"modal-footer\">\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\n data-testid=\"new-support-close-btn\"></button>\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"] }]
13755
+ args: [{ selector: 'app-support-modal', template: "<div class=\"modal-header\">\r\n <h5 class=\"modal-title\" translate=\"Help\"></h5>\r\n</div>\r\n<div class=\"modal-body\">\r\n <div class=\"support-loader-container\">\r\n <lib-loader *ngIf=\"loading\"></lib-loader>\r\n </div>\r\n <form (ngSubmit)=\"onSubmit()\" [formGroup]=\"supportForm\" style=\"margin-top:1rem;\" *ngIf=\"!loading && status==='new'\">\r\n <div>\r\n <label>{{'Title' | translate}} *</label>\r\n <div>\r\n <input type=\"text\" class=\"form-control\" formControlName=\"title\" data-testid=\"new-support-title\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['title'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-title-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['title'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['title'])\"></span>\r\n </div>\r\n </div>\r\n <div *ngIf=\"!currentUser\">\r\n <label>{{'Email' | translate}} *</label>\r\n <div>\r\n <input type=\"email\" class=\"form-control\" formControlName=\"email\" data-testid=\"new-support-email\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['email'])}\"\r\n maxlength=\"255\">\r\n </div>\r\n <div class=\"invalid-feedback\">\r\n <span data-testid=\"new-support-email-invalid\" [translate]=\"validatorsService.getError(supportForm.controls['email'])\"\r\n *ngIf=\" validatorsService.isControlError(supportForm.controls['email'])\"></span>\r\n </div>\r\n </div>\r\n <div>\r\n <label>{{'Description' | translate}} *</label>\r\n <div>\r\n <textarea formControlName=\"description\" class=\"form-control\"\r\n data-testid=\"new-support-description\"\r\n [ngClass]=\"{'is-invalid' : validatorsService.isControlError(supportForm.controls['description'])}\"></textarea>\r\n </div>\r\n <div class=\"invalid-feedback\" >\r\n <span data-testid=\"new-support-description-invalid\" [translate]=\" validatorsService.getError(supportForm.controls['description'])\"\r\n *ngIf=\"validatorsService.isControlError(supportForm.controls['description'])\"></span>\r\n </div>\r\n </div>\r\n </form>\r\n\r\n <div *ngIf=\"status ==='send'\" translate=\"Your request has been registered\" data-testid=\"new-support-send-info\">\r\n </div>\r\n <div *ngIf=\"status ==='error'\" translate=\"An error has occurred, please renew your request\"\r\n data-testid=\"new-support-error-info\">\r\n\r\n </div>\r\n</div>\r\n<div class=\"modal-footer\">\r\n <button type=\"button\" class=\"btn rounded-pill btn-museum\" data-dismiss=\"modal\" translate=\"Close\" (click)=\"onClose()\"\r\n data-testid=\"new-support-close-btn\"></button>\r\n <button *ngIf=\"!loading && status==='new'\" type=\"button\" class=\"btn rounded-pill btn-museum\" translate=\"Submit\"\r\n (click)=\"onSubmit()\" [disabled]=\"!supportForm.valid\" data-testid=\"new-support-submit-btn\"></button>\r\n</div>", styles: [".support-loader-container{width:88px;margin:auto}.invalid-feedback{height:16px}\n"] }]
13586
13756
  }], ctorParameters: () => [{ type: SupportService }, { type: i1$2.NgbActiveModal }, { type: i3.UntypedFormBuilder }, { type: BaseUserService }, { type: ValidatorsService }] });
13587
13757
 
13588
13758
  class LocaleService {
@@ -14993,11 +15163,11 @@ class NavigationBarComponent {
14993
15163
  this.baseVisibilityService.isChangePositionVisible.next(false);
14994
15164
  }
14995
15165
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NavigationBarComponent, deps: [{ token: 'currentAwsKinesisAnalyticsService' }, { token: i1$1.Router }, { token: SpaceService }, { token: BaseUserService }, { token: ZoneService }, { token: VisitService }, { token: ViewerService }, { token: NavigatorService }, { token: ZoneChangeService }, { token: BaseVisibilityService }], target: i0.ɵɵFactoryTarget.Component });
14996
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: NavigationBarComponent, selector: "lib-navigation-bar", viewQueries: [{ propertyName: "navbarSelectMenu", first: true, predicate: ["navbarSelectMenu"], descendants: true }], ngImport: i0, template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\n <div class=\"d-flex w-100\">\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\n </option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\n (ngModelChange)=\"onFloorClick()\">\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\n (ngModelChange)=\"onZoneClick()\">\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\n </select>\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\n <p class=\"fw-bold lh-base align-middle text-center\">\n {{ showLocation() }}\n </p>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n </div>\n </div>\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23565656;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23FFFFFF;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"], dependencies: [{ kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i11.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
15166
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: NavigationBarComponent, selector: "lib-navigation-bar", viewQueries: [{ propertyName: "navbarSelectMenu", first: true, predicate: ["navbarSelectMenu"], descendants: true }], ngImport: i0, template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\r\n <div class=\"d-flex w-100\">\r\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\r\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\r\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\r\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\r\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\r\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\r\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\r\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\r\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\r\n </option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\r\n (ngModelChange)=\"onFloorClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\r\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\r\n (ngModelChange)=\"onZoneClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\r\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\r\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\r\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\r\n </select>\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\r\n <p class=\"fw-bold lh-base align-middle text-center\">\r\n {{ showLocation() }}\r\n </p>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23565656;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23FFFFFF;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"], dependencies: [{ kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i11.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }] });
14997
15167
  }
14998
15168
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NavigationBarComponent, decorators: [{
14999
15169
  type: Component,
15000
- args: [{ selector: 'lib-navigation-bar', template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\n <div class=\"d-flex w-100\">\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\n </option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\n (ngModelChange)=\"onFloorClick()\">\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\n (ngModelChange)=\"onZoneClick()\">\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\n </select>\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\n </select>\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\n <p class=\"fw-bold lh-base align-middle text-center\">\n {{ showLocation() }}\n </p>\n </div>\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\n <button (click)=\"goTo()\"\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\n </button>\n </div>\n </div>\n </div>\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23565656;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0A<style type=\"text/css\">%0A%09.st0{fill:%23FFFFFF;}%0A<\\/style>%0A<g>%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0A%09%09/>%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0A</g>%0A</svg>%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"] }]
15170
+ args: [{ selector: 'lib-navigation-bar', template: "<nav class=\"navbar navbar-expand-md navbar-dark navbar-inverse second-nav\">\r\n <div class=\"d-flex w-100\">\r\n <div #navbarSelectMenu class=\"w-100\" id=\"nav-item-container-blue\">\r\n <div class=\"d-flex flex-column flex-md-row p-1 mt-2\">\r\n <select class=\"form-control flex-fill flex-lg-fill-unset location select-menu\"\r\n [(ngModel)]=\"selectedLocation\" (ngModelChange)=\"onLocationClick()\">\r\n <option [ngValue]=\"null\" selected>{{ 'Location' | translate }}</option>\r\n <option *ngFor=\"let loc of locations; index as index\" [ngValue]=\"loc\"> {{ loc.name }}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" (change)=\"onVisitClick($event)\"\r\n *ngIf=\"visitsPerSpace && visitsPerSpace.length > 1\">\r\n <option value=\"default\" selected disabled hidden>{{'Visit' | translate}}</option>\r\n <option *ngFor=\"let visit of visitsPerSpace; index as index\" value=\"{{index}}\">{{ visit.name}}\r\n </option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset floor select-menu\" [(ngModel)]=\"selectedFloor\"\r\n (ngModelChange)=\"onFloorClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Floor' | translate}}</option>\r\n <option *ngFor=\"let floor of floorsPerSpace\" [ngValue]=\"floor\">{{ floor.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu zone\" [(ngModel)]=\"selectedZone\"\r\n (ngModelChange)=\"onZoneClick()\">\r\n <option [ngValue]=\"null\" selected>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of zonesPerFloor\" [ngValue]=\"zone\"><span\r\n *ngIf=\"zone.code_ref\">{{zone.code_ref}}_</span>{{zone.name}}</option>\r\n </select>\r\n <select class=\"form-control flex-fill flex-lg-fill-unset select-menu\" [(ngModel)]=\"selectedChildZone\"\r\n *ngIf=\"childrenZones && childrenZones.length>0\" (ngModelChange)=\"onChildZoneClick()\">\r\n <option [ngValue]=\"null\" selected disabled>{{'Zone' | translate}}</option>\r\n <option *ngFor=\"let zone of childrenZones\" [ngValue]=\"zone\">{{ zone.name}}</option>\r\n </select>\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d d-none d-lg-block\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1\" *ngIf=\"showLocation()\">\r\n <p class=\"fw-bold lh-base align-middle text-center\">\r\n {{ showLocation() }}\r\n </p>\r\n </div>\r\n <div class=\"row align-items-center justify-content-center ml-0 p-1 d-block d-lg-none\">\r\n <button (click)=\"goTo()\"\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center btn-3d\"\r\n [disabled]=\"!selectedLocation || !selectedModel3d || !canGo()\">\r\n <i class=\"icon-go-to-3d d-inline-block\"></i> {{'3D visit' | translate}}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</nav>", styles: [":host{position:sticky;top:0;height:123px}.navbar{z-index:200;padding:.4rem 1rem}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0}@media (min-width: 992px){.flex-lg-fill-unset{flex:unset!important}}.form-control{width:100%;min-width:80px;max-width:18%}.location{width:auto;max-width:35%}.floor,.zone{width:auto;max-width:30%}button{color:#fff;padding:5px 10px;font-size:1.1em}.navbar-toggler{margin-right:1em}.select-menu{max-width:inherit}.btn-3d .icon-go-to-3d{width:30px;height:30px;background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23565656;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A');border:none;vertical-align:middle;fill:var(--smarterplan-primary)}.btn-3d:hover:enabled .icon-go-to-3d{background:url('data:image/svg+xml,<?xml version=\"1.0\" encoding=\"utf-8\"?>%0D%0A<!-- Generator: Adobe Illustrator 25.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->%0D%0A<svg version=\"1.1\" id=\"Calque_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\"%0D%0A%09 viewBox=\"0 0 60 60\" style=\"enable-background:new 0 0 60 60;\" xml:space=\"preserve\">%0D%0A<style type=\"text/css\">%0D%0A%09.st0{fill:%23FFFFFF;}%0D%0A<\\/style>%0D%0A<g>%0D%0A%09<path class=\"st0\" d=\"M43.9,32.1V17.7c0-0.4-0.2-0.7-0.5-0.9l-12.8-8c-0.2-0.1-0.4-0.2-0.6-0.2c-0.2,0-0.4,0.1-0.6,0.2l-12.8,8%0D%0A%09%09c-0.3,0.2-0.5,0.5-0.5,0.9v14.4c0,0.4,0.2,0.7,0.5,0.9l12.8,8c0.3,0.2,0.8,0.2,1.1,0l12.8-8C43.7,32.8,43.9,32.5,43.9,32.1z%0D%0A%09%09 M28.9,38.2l-10.7-6.7v-12l10.7,6.7V38.2z M30,24.4l-10.8-6.8L30,10.9l10.8,6.8L30,24.4z M41.8,31.5l-10.7,6.7v-12l10.7-6.7V31.5z\"%0D%0A%09%09/>%0D%0A%09<path class=\"st0\" d=\"M56.1,29.3l-10.3-6.8v2l1.4,1v0l0.8,0.5l0,0l7,4.8c0.3,0.2,0.4,0.5,0.4,0.8c0,0.3-0.2,0.6-0.4,0.8l-6.8,4.7%0D%0A%09%09l0,0l-0.9,0.7v0l-2.9,2v0l-13.8,9.6c-0.3,0.2-0.8,0.2-1.1,0l-14.2-9.8l-2.6-1.8v0L5,32.4c-0.3-0.2-0.4-0.5-0.4-0.8%0D%0A%09%09c0-0.3,0.2-0.6,0.4-0.8l0,0l7.3-5l0,0l0.5-0.3v0l1.4-1v-2L3.9,29.3c-0.8,0.5-1.2,1.4-1.2,2.4c0,0.9,0.5,1.8,1.2,2.4l8.8,6.1v0%0D%0A%09%09l4.5,3.1l11.2,7.7c1,0.7,2.3,0.7,3.3,0l13.7-9.5l1.9-1.3v0l8.8-6.1c0.8-0.5,1.2-1.4,1.2-2.4S56.9,29.8,56.1,29.3z\"/>%0D%0A</g>%0D%0A</svg>%0D%0A')}.btn-3d[disabled]:hover .icon-go-to-3d{fill:var(--smarterplan-primary)}.outer_circle{background-color:#ebebeb;border-radius:50%;width:32px;height:32px;padding:1px}@media (max-width: 767px){.navbar{padding:.2rem .5rem}.form-control,.location,.floor,.zone{max-width:100%;margin-bottom:15px}input,select{margin-right:0;margin-bottom:15px}}\n"] }]
15001
15171
  }], ctorParameters: () => [{ type: undefined, decorators: [{
15002
15172
  type: Inject,
15003
15173
  args: ['currentAwsKinesisAnalyticsService']
@@ -15266,11 +15436,11 @@ class MenuBarComponent {
15266
15436
  }
15267
15437
  }
15268
15438
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuBarComponent, deps: [{ token: i1$1.Router }, { token: LocaleService }, { token: BaseUserService }, { token: NavigatorService }, { token: SupportService }, { token: BaseVisibilityService }, { token: i1$2.NgbCalendar }, { token: i1$2.NgbDateParserFormatter }, { token: FilterService }, { token: AmplifyCacheService }], target: i0.ɵɵFactoryTarget.Component });
15269
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: MenuBarComponent, selector: "lib-menu-bar", inputs: { cacheName: "cacheName" }, ngImport: i0, template: "<div id=\"menu-bar\">\n <div *ngIf=\"!isVisitor\">\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\n </div>\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <p *ngIf=\"user && mission\" class=\"p-2\">\n <span class=\"username\">{{ user.displayName }}</span><br />\n <span class=\"user-role\">{{ mission.role }}</span><br />\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\n translate}}</span>\n </p>\n <div class=\"dropdown-divider\"></div>\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\n {{'Users management' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/profile\">\n {{'Profile' | translate}}</a>\n <div class=\"dropdown-divider\"></div>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\n [animation]=\"false\">\n <form\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\n (click)=\"onSubmit()\">{{'Search' |\n translate}}</button>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <div class=\"input-group date-selector m-1\">\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\n [value]=\"fromDate | formatDateNumberToDigits\"\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\n <ng-template #t let-date let-focused=\"focused\">\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\n (mouseleave)=\"hoveredDate = null\">\n {{ date.day }}\n </span>\n </ng-template>\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\n <div class=\"input-group date-selector m-1\">\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\n <button\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\n {{'Apply' | translate}}\n </button>\n <span\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n *ngIf=\"isDateFilterApplied && !dateChanged\">\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\n {{\"Applied\" | translate}}\n </span>\n <button\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\n |\n translate}}</button>\n <div\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\n for=\"btn-check-outlined\">{{\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\n </div>\n </div>\n </form>\n </div>\n </div>\n </nav>\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\n </div>\n <div *ngIf=\"isVisitor && isVisible\">\n <div class=\"visitor-div\">\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\n </div>\n <div class=\"avatar-container-menu\">\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$2.NgbCollapse, selector: "[ngbCollapse]", inputs: ["animation", "ngbCollapse", "horizontal"], outputs: ["ngbCollapseChange", "shown", "hidden"], exportAs: ["ngbCollapse"] }, { kind: "directive", type: i1$2.NgbInputDatepicker, selector: "input[ngbDatepicker]", inputs: ["autoClose", "contentTemplate", "datepickerClass", "dayTemplate", "dayTemplateData", "displayMonths", "firstDayOfWeek", "footerTemplate", "markDisabled", "minDate", "maxDate", "navigation", "outsideDays", "placement", "popperOptions", "restoreFocus", "showWeekNumbers", "startDate", "container", "positionTarget", "weekdays", "disabled"], outputs: ["dateSelect", "navigate", "closed"], exportAs: ["ngbDatepicker"] }, { kind: "component", type: AvatarComponent, selector: "lib-avatar", inputs: ["size", "userID"] }, { kind: "component", type: NavigationBarComponent, selector: "lib-navigation-bar" }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "pipe", type: FormatDateNumberToDigitsPipe, name: "formatDateNumberToDigits" }] });
15439
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: MenuBarComponent, selector: "lib-menu-bar", inputs: { cacheName: "cacheName" }, ngImport: i0, template: "<div id=\"menu-bar\">\r\n <div *ngIf=\"!isVisitor\">\r\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\r\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\r\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\r\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\r\n </div>\r\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\r\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <p *ngIf=\"user && mission\" class=\"p-2\">\r\n <span class=\"username\">{{ user.displayName }}</span><br />\r\n <span class=\"user-role\">{{ mission.role }}</span><br />\r\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\r\n translate}}</span>\r\n </p>\r\n <div class=\"dropdown-divider\"></div>\r\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\r\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\r\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\r\n {{'Users management' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/profile\">\r\n {{'Profile' | translate}}</a>\r\n <div class=\"dropdown-divider\"></div>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\r\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\r\n <span class=\"navbar-toggler-icon\"></span>\r\n </button>\r\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\r\n [animation]=\"false\">\r\n <form\r\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\r\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\r\n (click)=\"onSubmit()\">{{'Search' |\r\n translate}}</button>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\r\n [value]=\"fromDate | formatDateNumberToDigits\"\r\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\r\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\r\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\r\n <ng-template #t let-date let-focused=\"focused\">\r\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\r\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\r\n (mouseleave)=\"hoveredDate = null\">\r\n {{ date.day }}\r\n </span>\r\n </ng-template>\r\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\r\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\r\n <button\r\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\r\n {{'Apply' | translate}}\r\n </button>\r\n <span\r\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n *ngIf=\"isDateFilterApplied && !dateChanged\">\r\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\r\n {{\"Applied\" | translate}}\r\n </span>\r\n <button\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\r\n |\r\n translate}}</button>\r\n <div\r\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\r\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\r\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\r\n for=\"btn-check-outlined\">{{\r\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\r\n </div>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </nav>\r\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\r\n </div>\r\n <div *ngIf=\"isVisitor && isVisible\">\r\n <div class=\"visitor-div\">\r\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\r\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\r\n </div>\r\n <div class=\"avatar-container-menu\">\r\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"], dependencies: [{ kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i11.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$2.NgbCollapse, selector: "[ngbCollapse]", inputs: ["animation", "ngbCollapse", "horizontal"], outputs: ["ngbCollapseChange", "shown", "hidden"], exportAs: ["ngbCollapse"] }, { kind: "directive", type: i1$2.NgbInputDatepicker, selector: "input[ngbDatepicker]", inputs: ["autoClose", "contentTemplate", "datepickerClass", "dayTemplate", "dayTemplateData", "displayMonths", "firstDayOfWeek", "footerTemplate", "markDisabled", "minDate", "maxDate", "navigation", "outsideDays", "placement", "popperOptions", "restoreFocus", "showWeekNumbers", "startDate", "container", "positionTarget", "weekdays", "disabled"], outputs: ["dateSelect", "navigate", "closed"], exportAs: ["ngbDatepicker"] }, { kind: "component", type: AvatarComponent, selector: "lib-avatar", inputs: ["size", "userID"] }, { kind: "component", type: NavigationBarComponent, selector: "lib-navigation-bar" }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "pipe", type: FormatDateNumberToDigitsPipe, name: "formatDateNumberToDigits" }] });
15270
15440
  }
15271
15441
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuBarComponent, decorators: [{
15272
15442
  type: Component,
15273
- args: [{ selector: 'lib-menu-bar', template: "<div id=\"menu-bar\">\n <div *ngIf=\"!isVisitor\">\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\n </div>\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <p *ngIf=\"user && mission\" class=\"p-2\">\n <span class=\"username\">{{ user.displayName }}</span><br />\n <span class=\"user-role\">{{ mission.role }}</span><br />\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\n translate}}</span>\n </p>\n <div class=\"dropdown-divider\"></div>\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\n {{'Users management' | translate}}</a>\n <a class=\"dropdown-item\" routerLink=\"/profile\">\n {{'Profile' | translate}}</a>\n <div class=\"dropdown-divider\"></div>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\n <span class=\"navbar-toggler-icon\"></span>\n </button>\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\n [animation]=\"false\">\n <form\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\n (click)=\"onSubmit()\">{{'Search' |\n translate}}</button>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\n <div class=\"input-group date-selector m-1\">\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\n [value]=\"fromDate | formatDateNumberToDigits\"\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\n <ng-template #t let-date let-focused=\"focused\">\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\n (mouseleave)=\"hoveredDate = null\">\n {{ date.day }}\n </span>\n </ng-template>\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\n <div class=\"input-group date-selector m-1\">\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\n <div class=\"input-group-append\">\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\n type=\"button\"></button>\n </div>\n </div>\n </div>\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\n <button\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\n {{'Apply' | translate}}\n </button>\n <span\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n *ngIf=\"isDateFilterApplied && !dateChanged\">\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\n {{\"Applied\" | translate}}\n </span>\n <button\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\n |\n translate}}</button>\n <div\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\n for=\"btn-check-outlined\">{{\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\n </div>\n </div>\n </form>\n </div>\n </div>\n </nav>\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\n </div>\n <div *ngIf=\"isVisitor && isVisible\">\n <div class=\"visitor-div\">\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\n </div>\n <div class=\"avatar-container-menu\">\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\n <lib-avatar></lib-avatar>\n </div>\n <div class=\"dropdown-menu dropdown-menu-right\">\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\n translate}}</button>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"] }]
15443
+ args: [{ selector: 'lib-menu-bar', template: "<div id=\"menu-bar\">\r\n <div *ngIf=\"!isVisitor\">\r\n <nav class=\"navbar navbar-expand-md navbar-light p-0\">\r\n <div class=\"container-fluid\" *ngIf=\"isVisible\">\r\n <div class=\"navbar-brand order-md-0\" [routerLink]=\"['/dashboard']\">\r\n <img alt=\"SmarterPlan\" src=\"../assets/images/logos/logo-immo.png\">\r\n </div>\r\n <div class=\"ms-auto nav-item dropdown profile_pic-container order-md-2\">\r\n <div class=\"profile_pic dropdown-toggle\" data-testid=\"dropdown-menu-button\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <p *ngIf=\"user && mission\" class=\"p-2\">\r\n <span class=\"username\">{{ user.displayName }}</span><br />\r\n <span class=\"user-role\">{{ mission.role }}</span><br />\r\n <span class=\"org\">{{ mission.organisation ? mission.organisation.name : \"No organisation\" |\r\n translate}}</span>\r\n </p>\r\n <div class=\"dropdown-divider\"></div>\r\n <!-- <a class=\"dropdown-item\" routerLink=\"/debug\" *ngIf=\"isManager\">Debug</a> -->\r\n <a class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/dashboard\">{{'Data management' | translate}}</a>\r\n <a class=\"dropdown-item\" data-testid=\"user-management-btn\" routerLink=\"/admin\" *ngIf=\"isManager\">\r\n {{'Users management' | translate}}</a>\r\n <a class=\"dropdown-item\" routerLink=\"/profile\">\r\n {{'Profile' | translate}}</a>\r\n <div class=\"dropdown-divider\"></div>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n <button class=\"navbar-toggler order-md-1\" (click)=\"navbarCollapse.toggle()\" type=\"button\"\r\n [attr.aria-expanded]=\"!isCollapsed\" aria-controls=\"navbar-collapse\">\r\n <span class=\"navbar-toggler-icon\"></span>\r\n </button>\r\n <div class=\"navbar-collapse order-md-1\" #navbarCollapse=\"ngbCollapse\" [(ngbCollapse)]=\"isCollapsed\"\r\n [animation]=\"false\">\r\n <form\r\n class=\"form-inline m-0 mt-2 form-container d-flex justify-content-start align-items-start align-items-md-center flex-column flex-md-row flex-nowrap filters-menu-block\">\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <input class=\"form-control search-button m-1\" placeholder=\"keyword\" id=\"mainSearchBar\" type=\"text\">\r\n <button class=\"btn btn-outline-primary rounded-pill m-1 w-fit-content h-fit-content align-self-md-center\"\r\n (click)=\"onSubmit()\">{{'Search' |\r\n translate}}</button>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-0\">\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpFromDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpFromDate\"\r\n [value]=\"fromDate | formatDateNumberToDigits\"\r\n (input)=\"fromDate = validateInput(fromDate, dpFromDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n <input name=\"datepicker\" class=\"form-control visually-hidden\" ngbDatepicker #datepicker=\"ngbDatepicker\"\r\n [autoClose]=\"'outside'\" (dateSelect)=\"onDateSelection($event)\" [displayMonths]=\"2\" [dayTemplate]=\"t\"\r\n outsideDays=\"hidden\" [startDate]=\"fromDate || calendar.getToday()\" [container]=\"'body'\" tabindex=\"-1\">\r\n <ng-template #t let-date let-focused=\"focused\">\r\n <span class=\"custom-day\" [class.focused]=\"focused\" [class.range]=\"isRange(date)\"\r\n [class.faded]=\"isHovered(date) || isInside(date)\" (mouseenter)=\"hoveredDate = date\"\r\n (mouseleave)=\"hoveredDate = null\">\r\n {{ date.day }}\r\n </span>\r\n </ng-template>\r\n <span class=\"ms-1 p-2\">{{'to' | translate}}</span>\r\n <div class=\"input-group date-selector m-1\">\r\n <input #dpToDate class=\"form-control\" placeholder=\"dd/MM/yyyy\" name=\"dpToDate\"\r\n [value]=\"toDate | formatDateNumberToDigits\" (input)=\"toDate = validateInput(toDate, dpToDate.value)\">\r\n <div class=\"input-group-append\">\r\n <button class=\"btn btn-outline-secondary calendar\" (click)=\"datepicker.toggle()\"\r\n type=\"button\"></button>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"input-group search-button-container d-flex flex-row flex-nowrap p-0 m-1 justify-content-evenly\">\r\n <button\r\n class=\"btn btn-primary rounded-pill ms-1 w-25 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onApply()\" *ngIf=\"!isDateFilterApplied || dateChanged\">\r\n {{'Apply' | translate}}\r\n </button>\r\n <span\r\n class=\"appliedText ms-1 py-2 align-self-md-center pb-md-0 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n *ngIf=\"isDateFilterApplied && !dateChanged\">\r\n <span class=\"iconify\" data-icon=\"ion:checkmark\" data-width=\"15\" data-height=\"15\"></span>\r\n {{\"Applied\" | translate}}\r\n </span>\r\n <button\r\n class=\"btn btn-outline-primary rounded-pill w-25 mx-2 w-md-fit-content h-md-fit-content align-self-md-center\"\r\n type=\"button\" (click)=\"onClearDates()\">{{'Clear'\r\n |\r\n translate}}</button>\r\n <div\r\n class=\"change-position-button align-self-end pb-2 align-self-md-center mx-2 pb-md-0 flex-nowrap w-25 w-md-fit-content h-md-fit-content\">\r\n <input type=\"checkbox\" class=\"btn-check\" id=\"btn-check-outlined\" autocomplete=\"off\">\r\n <label class=\"link-primary cursor-pointer text-decoration-underline\" (click)=\"onChangePositionClick()\"\r\n for=\"btn-check-outlined\">{{\r\n (isChangePositionVisible ? \"Close\" : \"Move to\") | translate }}</label>\r\n </div>\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </nav>\r\n <lib-navigation-bar *ngIf=\"isVisible && isChangePositionVisible\"></lib-navigation-bar>\r\n </div>\r\n <div *ngIf=\"isVisitor && isVisible\">\r\n <div class=\"visitor-div\">\r\n <div class=\"logo-container\" data-testid=\"dropdown-menu-button\">\r\n <img class=\"img-logo\" alt=\"SmarterPlanLogo\" src=\"../assets/images/logos/logo-reserve.png\">\r\n </div>\r\n <div class=\"avatar-container-menu\">\r\n <div class=\"profile_pic dropdown-toggle cursor-pointer\" data-bs-toggle=\"dropdown\">\r\n <lib-avatar></lib-avatar>\r\n </div>\r\n <div class=\"dropdown-menu dropdown-menu-right\">\r\n <button class=\"dropdown-item\" (click)=\"localeService.modal()\">{{'Language' | translate}}</button>\r\n <button class=\"dropdown-item\" (click)=\"openSupportForm()\">{{\"Help\" | translate}}</button>\r\n <button data-testid=\"logout-auth-btn\" class=\"dropdown-item\" (click)=\"signOut()\">{{\"Logout\" |\r\n translate}}</button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: ["#menu-bar{position:sticky;top:0;z-index:200}.navbar{z-index:200}.navbar-brand{padding-top:5px;padding-bottom:5px;cursor:pointer}.navbar-brand img{height:70px}.profile_pic{border-radius:50%;height:45px;width:45px;margin:8px;border:.2px solid rgb(150,150,150)}.navbar-nav .profile-pic-container .dropdown-menu{position:absolute}.navbar.second-nav{z-index:199;background-color:var(--smarterplan-primary)}.profile_pic.dropdown-toggle:after{display:none}.navbar-light{border-bottom:1px solid var(--smarterplan-primary);background-color:#fff}.navbar form{width:100%;margin:0;padding:0}input,select{margin-right:15px}.select{background:transparent;border:none;color:#fff;margin:6px 20px 0;width:auto}button{padding:5px 10px}button:disabled,button[disabled]{color:#c04f4f}.navbar-toggler{margin-right:1em}.icon{height:40px;width:40px;color:var(--smarterplan-primary)}.search-button-container{width:auto;align-items:stretch;justify-items:start}.search-button-container .search-button{width:auto;border-radius:1rem!important}.visitor-div{background-color:var(--smarterplan-primary);height:60px;display:flex;justify-content:space-between}.profile_pic-container{margin-right:1em}.logo-container,.img-logo{height:100%}.change-position-button{margin-left:2rem}.change-position-button label,.change-position-button .btn-check:checked,.change-position-button .btn-check:focus,.change-position-button .btn-outline-primary,.change-position-button .btn{box-shadow:none!important;outline:0}.change-position-button label{border-radius:16px}.change-position-button .btn-check:checked+.btn{background-color:var(--smarterplan-primary);color:#fff!important}.filters-menu-block{width:auto}@media (min-width: 1200px){.navbar-brand img{height:100px}.profile_pic{height:60px;width:60px;margin:15px}}@media (min-width: 992px) and (max-width: 1199.98px){.navbar-brand img{height:80px}.profile_pic{height:50px;width:50px;margin:12px}}@media (min-width: 768px) and (max-width: 991.98px){.navbar-brand img{height:60px}.profile_pic{height:40px;width:40px;margin:10px}}@media (max-width: 767.98px){.navbar-brand img{height:50px}.profile_pic{height:30px;width:30px;margin:8px}.search-button-container,.filters-menu-block{width:100%}}.form-container{margin:.5rem 0}.navbar-collapse.show{display:flex;padding-bottom:.5rem}.hidden{width:0;margin:0;border:none;padding:0}.custom-day{text-align:center;padding:.185rem .25rem;display:inline-block;height:2rem;width:2rem}.custom-day.focused{background-color:#e6e6e6}.custom-day.range,.custom-day:hover{background-color:var(--smarterplan-primary-lighter);color:#fff}.custom-day.faded{background-color:var(--smarterplan-primary-transparent)}.calendar{height:100%}.appliedText,.appliedText span{color:var(--smarterplan-primary)}@media (max-width: 767px){.form-inline{flex-direction:column}}.hidden{opacity:0}.date-selector{flex-wrap:nowrap}.date-selector input{min-width:80px;margin:0}@media (min-width: 768px){.w-md-fit-content{width:fit-content!important}.h-md-content{height:fit-content!important}}\n"] }]
15274
15444
  }], ctorParameters: () => [{ type: i1$1.Router }, { type: LocaleService }, { type: BaseUserService }, { type: NavigatorService }, { type: SupportService }, { type: BaseVisibilityService }, { type: i1$2.NgbCalendar }, { type: i1$2.NgbDateParserFormatter }, { type: FilterService }, { type: AmplifyCacheService }], propDecorators: { cacheName: [{
15275
15445
  type: Input
15276
15446
  }] } });
@@ -15425,27 +15595,27 @@ class BaseTagService {
15425
15595
  throw new Error('updatePoiMetadata is not implemented in the base class.');
15426
15596
  }*/
15427
15597
  async prepareEquipmentHtml(equip) {
15428
- throw new Error(`prepareEquipmentHtml is not implemented in the base class.
15598
+ throw new Error(`prepareEquipmentHtml is not implemented in the base class.
15429
15599
  Please override this method in your extended class.`);
15430
15600
  }
15431
15601
  async prepareTicketHtml(ticket) {
15432
- throw new Error(`prepareTicketHtml is not implemented in the base class.
15602
+ throw new Error(`prepareTicketHtml is not implemented in the base class.
15433
15603
  Please override this method in your extended class.`);
15434
15604
  }
15435
15605
  async prepareFeatureHtml(feature) {
15436
- throw new Error(`prepareFeatureHtml is not implemented in the base class.
15606
+ throw new Error(`prepareFeatureHtml is not implemented in the base class.
15437
15607
  Please override this method in your extended class.`);
15438
15608
  }
15439
15609
  async prepareMeasurementHtml(measure) {
15440
- throw new Error(`prepareMeasurementHtml is not implemented in the base class.
15610
+ throw new Error(`prepareMeasurementHtml is not implemented in the base class.
15441
15611
  Please override this method in your extended class.`);
15442
15612
  }
15443
15613
  async prepareDeskHtml(feature) {
15444
- throw new Error(`prepareDeskHtml is not implemented in the base class.
15614
+ throw new Error(`prepareDeskHtml is not implemented in the base class.
15445
15615
  Please override this method in your extended class.`);
15446
15616
  }
15447
15617
  async prepareIndicatorHtml(feature) {
15448
- throw new Error(`prepareDeskHtml is not implemented in the base class.
15618
+ throw new Error(`prepareDeskHtml is not implemented in the base class.
15449
15619
  Please override this method in your extended class.`);
15450
15620
  }
15451
15621
  async getSignedTagIconSource(tagIconSrc) {
@@ -15454,15 +15624,15 @@ class BaseTagService {
15454
15624
  getIconTagImageForFeature(feature, poi) {
15455
15625
  // const tagIcon = JSON.parse(poi.tagIcon);
15456
15626
  // return tagIcon.src;
15457
- throw new Error(`getIconTagImageForFeature is not implemented in the base class.
15627
+ throw new Error(`getIconTagImageForFeature is not implemented in the base class.
15458
15628
  Please override this method in your extended class.`);
15459
15629
  }
15460
15630
  getScriptForTag(object, tagType) {
15461
- throw new Error(`getScriptForTag is not implemented in the base class.
15631
+ throw new Error(`getScriptForTag is not implemented in the base class.
15462
15632
  Please override this method in your extended class.`);
15463
15633
  }
15464
15634
  getAnnexeForCommentTypeInFeature(feature, commentType) {
15465
- throw new Error(`getAnnexeForCommentTypeInFeature is not implemented in the base class.
15635
+ throw new Error(`getAnnexeForCommentTypeInFeature is not implemented in the base class.
15466
15636
  Please override this method in your extended class.`);
15467
15637
  }
15468
15638
  getBillboardMediaToEmbed(object) {
@@ -15471,27 +15641,27 @@ class BaseTagService {
15471
15641
  return { comment: null, tagDescription: '' };
15472
15642
  }
15473
15643
  onActionDetailClick(url) {
15474
- throw new Error(`onActionDetailClick is not implemented in the base class.
15644
+ throw new Error(`onActionDetailClick is not implemented in the base class.
15475
15645
  Please override this method in your extended class.`);
15476
15646
  }
15477
15647
  onActionAudioClick(audioCommentID) {
15478
- throw new Error(`onActionDetailClick is not implemented in the base class.
15648
+ throw new Error(`onActionDetailClick is not implemented in the base class.
15479
15649
  Please override this method in your extended class.`);
15480
15650
  }
15481
15651
  onActionVideoClick(url) {
15482
- throw new Error(`onActionVideoClick is not implemented in the base class.
15652
+ throw new Error(`onActionVideoClick is not implemented in the base class.
15483
15653
  Please override this method in your extended class.`);
15484
15654
  }
15485
15655
  onActionImageClick(imageCommentID) {
15486
- throw new Error(`onActionImageClick is not implemented in the base class.
15656
+ throw new Error(`onActionImageClick is not implemented in the base class.
15487
15657
  Please override this method in your extended class.`);
15488
15658
  }
15489
15659
  onActionDocClick(url) {
15490
- throw new Error(`onActionDocClick is not implemented in the base class.
15660
+ throw new Error(`onActionDocClick is not implemented in the base class.
15491
15661
  Please override this method in your extended class.`);
15492
15662
  }
15493
15663
  onActionYoutubeClick(url) {
15494
- throw new Error(`onActionYoutubeClick is not implemented in the base class.
15664
+ throw new Error(`onActionYoutubeClick is not implemented in the base class.
15495
15665
  Please override this method in your extended class.`);
15496
15666
  }
15497
15667
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BaseTagService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -15599,6 +15769,11 @@ class MatterportImportService {
15599
15769
  }
15600
15770
  async initSdkForModel(modelID) {
15601
15771
  this.stop = false;
15772
+ // This service is a singleton: without a reset, the readiness wait below was satisfied at
15773
+ // once by the sweeps/floors of the PREVIOUS import, and the new import ran on stale data.
15774
+ this.sweeps = undefined;
15775
+ this.floors = undefined;
15776
+ this.currentFloor = undefined;
15602
15777
  // Load SDK
15603
15778
  console.log('Loading Matterport SDK');
15604
15779
  this.modelID = modelID;
@@ -15636,8 +15811,9 @@ class MatterportImportService {
15636
15811
  }.bind(this),
15637
15812
  });
15638
15813
  await this.sdk.Settings.update('labels', false);
15814
+ // The showcase can take well over 7 s to publish its sweeps on a slow connection.
15639
15815
  let timeoutExpired = false;
15640
- setTimeout(() => (timeoutExpired = true), 7000);
15816
+ setTimeout(() => (timeoutExpired = true), 20000);
15641
15817
  await waitUntil(() => (this.sweeps !== undefined && this.floors !== undefined) ||
15642
15818
  timeoutExpired === true);
15643
15819
  if (timeoutExpired) {
@@ -15684,7 +15860,8 @@ class MatterportImportService {
15684
15860
  const layerBuilding = await this.getLayerByName('BUILDING');
15685
15861
  const createdDefaultZone = await this.createZone(node.metadata, layerBuilding.id, surface, mainZone.id, Object.keys(this.sweeps), node);
15686
15862
  if (!createdDefaultZone) {
15687
- return Promise.resolve(false);
15863
+ // Nothing else can be imported without this zone: fail loudly instead of "succeeding"
15864
+ return Promise.reject(new Error('the zone of the visit could not be created'));
15688
15865
  }
15689
15866
  if (this.floors) {
15690
15867
  floorZones = await this.importFloorData(createdDefaultZone.id, node);
@@ -16736,6 +16913,7 @@ class MatterportTagCopyService {
16736
16913
  operationService;
16737
16914
  domainService;
16738
16915
  translate;
16916
+ zoneService;
16739
16917
  // Emits progress updates during copyTagsFromVisit(); null when idle
16740
16918
  copyProgress$ = new BehaviorSubject(null);
16741
16919
  // Emits the accumulated log lines during copyTagsFromVisit(); [] when idle
@@ -16795,7 +16973,7 @@ class MatterportTagCopyService {
16795
16973
  targetDomainIdsByTypeAndName = new Map();
16796
16974
  /** Target space id of the run in progress — used to re-home copied tag icons. */
16797
16975
  targetSpaceId = null;
16798
- constructor(navigationService, userService, poiService, ticketService, equipmentService, object3DService, featureService, measurementService, commentService, hashtagService, missionService, operationService, domainService, translate) {
16976
+ constructor(navigationService, userService, poiService, ticketService, equipmentService, object3DService, featureService, measurementService, commentService, hashtagService, missionService, operationService, domainService, translate, zoneService) {
16799
16977
  this.navigationService = navigationService;
16800
16978
  this.userService = userService;
16801
16979
  this.poiService = poiService;
@@ -16810,6 +16988,7 @@ class MatterportTagCopyService {
16810
16988
  this.operationService = operationService;
16811
16989
  this.domainService = domainService;
16812
16990
  this.translate = translate;
16991
+ this.zoneService = zoneService;
16813
16992
  this.spModule = userService.getSpModule();
16814
16993
  }
16815
16994
  //
@@ -16818,6 +16997,8 @@ class MatterportTagCopyService {
16818
16997
  async copyTagsFromVisit(sourceSpaceId, targetSpaceId, landmarkSourceA, landmarkTargetA, landmarkSourceB, landmarkTargetB) {
16819
16998
  let errorCount = 0;
16820
16999
  let healedCount = 0;
17000
+ // items skipped because an equivalent already exists in the target (earlier run)
17001
+ let alreadyPresentCount = 0;
16821
17002
  this.resetLog();
16822
17003
  this.resetRunState(targetSpaceId);
16823
17004
  try {
@@ -16886,8 +17067,9 @@ class MatterportTagCopyService {
16886
17067
  // loadDomainIndex() for why this is keyed to Mission, not to the Space itself.
16887
17068
  await this.loadDomainIndex(sourceSpaceId, targetSpaceId);
16888
17069
  if (this.spModule == SpModule.IMMO) {
16889
- sourceMeasurements = await this.measurementService.getMeasurementsForSpaceForUser(sourceSpaceId);
16890
- targetMeasurements = await this.measurementService.getMeasurementsForSpaceForUser(targetSpaceId);
17070
+ sourceMeasurements = (await this.measurementService.getMeasurementsForSpaceForUser(sourceSpaceId))
17071
+ .filter(m => (m.numberPoints ?? 0) > 0);
17072
+ targetMeasurements = (await this.measurementService.getMeasurementsForSpaceForUser(targetSpaceId));
16891
17073
  }
16892
17074
  this.setStepProgress('collecting', 'collecting', 90);
16893
17075
  const sourcePoisAll = await this.poiService.getPoisByMatterportSweeps(sourceSweepIds);
@@ -16951,6 +17133,9 @@ class MatterportTagCopyService {
16951
17133
  const sourceObj = sourceTickets[i];
16952
17134
  try {
16953
17135
  const existingMatch = this.findExisting(sourceObj, targetTickets, (t) => [t.title, t.description]);
17136
+ if (existingMatch) {
17137
+ alreadyPresentCount++;
17138
+ }
16954
17139
  if (!existingMatch) {
16955
17140
  const rawSource = sourceObj;
16956
17141
  const ticketInput = {
@@ -17070,6 +17255,9 @@ class MatterportTagCopyService {
17070
17255
  // Equipment has no `title`/`description`: it identifies itself by name, and by
17071
17256
  // serial number when one is recorded.
17072
17257
  const existingMatch = this.findExisting(sourceObj, targetEquipments, (e) => [e.name, e.serialNumber]);
17258
+ if (existingMatch) {
17259
+ alreadyPresentCount++;
17260
+ }
17073
17261
  if (!existingMatch) {
17074
17262
  const rawSource = sourceObj;
17075
17263
  let copiedPhotoKey = null;
@@ -17249,6 +17437,9 @@ class MatterportTagCopyService {
17249
17437
  const sourceObj = sourceObjects3D[i];
17250
17438
  try {
17251
17439
  const existsInTarget = targetObjects3D.some(targetObj => targetObj.name === sourceObj.name);
17440
+ if (existsInTarget) {
17441
+ alreadyPresentCount++;
17442
+ }
17252
17443
  if (!existsInTarget) {
17253
17444
  const rawSource = sourceObj;
17254
17445
  // `path` means two different things depending on `s3Bucket`, exactly as the
@@ -17366,6 +17557,9 @@ class MatterportTagCopyService {
17366
17557
  const sourceObj = sourceFeatures[i];
17367
17558
  try {
17368
17559
  const existingMatch = this.findExisting(sourceObj, targetFeatures, (f) => [f.title, f.description]);
17560
+ if (existingMatch) {
17561
+ alreadyPresentCount++;
17562
+ }
17369
17563
  if (!existingMatch) {
17370
17564
  const rawSource = sourceObj;
17371
17565
  // Resolved just before the write: a keyword is only created in the target
@@ -17470,6 +17664,9 @@ class MatterportTagCopyService {
17470
17664
  const sourceMeasurement = sourceMeasurements[i];
17471
17665
  try {
17472
17666
  const existsInTarget = targetMeasurements.some(tm => tm.comment.title === sourceMeasurement.comment.title);
17667
+ if (existsInTarget) {
17668
+ alreadyPresentCount++;
17669
+ }
17473
17670
  if (!existsInTarget) {
17474
17671
  const rawComment = sourceMeasurement.comment;
17475
17672
  let copiedAnnexe = null;
@@ -17529,7 +17726,15 @@ class MatterportTagCopyService {
17529
17726
  for (let i = 0; i < sourcePois.length; i++) {
17530
17727
  const sourcePoi = sourcePois[i];
17531
17728
  try {
17532
- const existsInTarget = targetPois.some(tp => tp.coordinate === sourcePoi.coordinate && tp.tagDescription === sourcePoi.tagDescription);
17729
+ // Compared on the parsed position, where the copy WOULD land: the stored strings
17730
+ // differ in format ('{ "x":' vs '{"x":') and, once landmarks move the tags, the
17731
+ // source string never equals its copy's, so every re-run duplicated these POIs.
17732
+ const expectedPosition = this.parseCoordinate(this.getAdjustedPositionOnly(sourcePoi.coordinate, transform));
17733
+ const existsInTarget = targetPois.some(tp => tp.tagDescription === sourcePoi.tagDescription
17734
+ && this.isSamePosition(this.parseCoordinate(tp.coordinate), expectedPosition));
17735
+ if (existsInTarget) {
17736
+ alreadyPresentCount++;
17737
+ }
17533
17738
  if (!existsInTarget) {
17534
17739
  const elementID = sourcePoi.elementID
17535
17740
  ? (elementIds[sourcePoi.elementID] || sourcePoi.elementID)
@@ -17564,6 +17769,10 @@ class MatterportTagCopyService {
17564
17769
  if (healedCount > 0) {
17565
17770
  this.setStepLog('healedSummary', this.translate.instant('matterportCopy.healedSummary', { count: healedCount }));
17566
17771
  }
17772
+ // Says plainly that a re-run created nothing new, instead of a bare "success".
17773
+ if (alreadyPresentCount > 0) {
17774
+ this.setStepLog('alreadyPresentSummary', this.translate.instant('matterportCopy.alreadyPresentSummary', { count: alreadyPresentCount }));
17775
+ }
17567
17776
  return true;
17568
17777
  }
17569
17778
  catch (error) {
@@ -18320,6 +18529,28 @@ class MatterportTagCopyService {
18320
18529
  yRotation: applyRigidTransformToYRotationDeg(parsed.yRotation, transform)
18321
18530
  };
18322
18531
  }
18532
+ /**
18533
+ * Reads a stored coordinate (JSON string or object, whatever its spacing) as a position.
18534
+ * Null for a virtual POI or an unreadable value.
18535
+ */
18536
+ parseCoordinate(value) {
18537
+ try {
18538
+ const parsed = typeof value === 'string' ? JSON.parse(value) : value;
18539
+ return parsed && typeof parsed.x === 'number'
18540
+ ? { x: parsed.x, y: parsed.y, z: parsed.z }
18541
+ : null;
18542
+ }
18543
+ catch {
18544
+ return null;
18545
+ }
18546
+ }
18547
+ /** Two positions within 1 cm are the same (two virtual POIs, both null, are too). */
18548
+ isSamePosition(a, b, toleranceM = 0.01) {
18549
+ if (!a || !b) {
18550
+ return !a && !b;
18551
+ }
18552
+ return distance(a, b) <= toleranceM;
18553
+ }
18323
18554
  /**
18324
18555
  * Applies the rigid transform's translation+rotation to a plain {x,y,z} position,
18325
18556
  * WITHOUT adding xRotation/yRotation keys. Used for measurements, whose stored
@@ -18545,13 +18776,403 @@ class MatterportTagCopyService {
18545
18776
  // for the CopySource header. Do NOT re-encode it here.
18546
18777
  return extracted;
18547
18778
  }
18548
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportTagCopyService, deps: [{ token: NavigationService }, { token: BaseUserService }, { token: PoiService }, { token: TicketsService }, { token: EquipmentService }, { token: Object3DService }, { token: FeatureService }, { token: MeasurementService }, { token: CommentService }, { token: HashtagService }, { token: MissionService }, { token: OperationService }, { token: DomainService }, { token: i1.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable });
18779
+ /**
18780
+ * Both tag lists a recalibration can offer for a target space, computed in one pass. Only
18781
+ * the target location's own tags are ever listed (see RecalibrationTagScope); tags whose
18782
+ * element was deleted — orphan POIs — never are.
18783
+ * - `space`: every tag of the location, in any of its visits;
18784
+ * - `visit`: the tags of the location sitting on the sweeps of the selected target visit
18785
+ * (`options.targetNodeId`); empty when no visit is given.
18786
+ * With `options.onlyTagsInSource` and a source other than the target, both lists only keep
18787
+ * the tags that also exist in the source location (same identity as the copy's duplicate
18788
+ * detection): typically the tags an earlier copy brought over.
18789
+ *
18790
+ * A POI carries no name of its own: each tag gets `elementName`, the name of the element
18791
+ * it points at (ticket title, equipment name, ...).
18792
+ */
18793
+ async getRecalibrationTagSets(sourceSpaceId, targetSpaceId, options = {}) {
18794
+ if (!sourceSpaceId || !targetSpaceId) {
18795
+ return { space: [], visit: [] };
18796
+ }
18797
+ const filterBySource = !!options.onlyTagsInSource && sourceSpaceId !== targetSpaceId;
18798
+ const [targetTags, sourceTags] = await Promise.all([
18799
+ this.loadLiveTagsForSpace(targetSpaceId, options.targetNodeId),
18800
+ filterBySource ? this.loadLiveTagsForSpace(sourceSpaceId) : Promise.resolve(null)
18801
+ ]);
18802
+ const sourceKeys = sourceTags && new Set(sourceTags.filter(tag => tag.ownedBySpace).map(tag => tag.identity));
18803
+ const ownTargetTags = targetTags
18804
+ .filter(tag => tag.ownedBySpace)
18805
+ .filter(tag => !sourceKeys || sourceKeys.has(tag.identity));
18806
+ return {
18807
+ space: ownTargetTags.map(tag => tag.poi),
18808
+ visit: ownTargetTags.filter(tag => tag.inVisit).map(tag => tag.poi)
18809
+ };
18810
+ }
18811
+ /**
18812
+ * Signed, displayable URL of a tag's picture (`elementImageKey`). Meant to be called on
18813
+ * demand — when the tag is previewed — rather than for the whole list.
18814
+ */
18815
+ async getTagImageUrl(imageKey) {
18816
+ const key = this.extractS3KeyFromValue(imageKey);
18817
+ return key ? getSignedFile(key) : null;
18818
+ }
18819
+ /**
18820
+ * Loads the POIs placed on the sweeps of a space whose element still exists, each with a
18821
+ * display name, an identity key and whether its element belongs to that space. The key
18822
+ * follows the same rules as the copy's duplicate detection (findExisting), so a tag and its
18823
+ * copy in another space share the same key. With a visit node, `inVisit` tells whether the
18824
+ * tag sits on one of that visit's sweeps.
18825
+ */
18826
+ async loadLiveTagsForSpace(spaceId, visitNodeId) {
18827
+ const [pois, elements, zones] = await Promise.all([
18828
+ this.navigationService.getAllowedSweepsIDsForUserInSpace(spaceId)
18829
+ .then(sweepIds => this.poiService.getPoisByMatterportSweeps(sweepIds)),
18830
+ this.getTagElementsForSpace(spaceId),
18831
+ this.zoneService.getZonesBySpace(spaceId).catch(error => {
18832
+ console.error('Failed to load the zones of the space:', error);
18833
+ return [];
18834
+ })
18835
+ ]);
18836
+ const sweepZones = this.getSweepZones(zones || []);
18837
+ const visitSweeps = visitNodeId ? await this.getSweepsOfVisit(visitNodeId, zones || []) : new Set();
18838
+ // A POI can point at an element stored under another space (left behind by an earlier
18839
+ // copy): those are not in the space-wide lists and are fetched by ID.
18840
+ const unresolved = (pois || []).filter(poi => poi?.elementID && !elements.has(poi.elementID));
18841
+ await this.fetchTagElementsById(unresolved, elements);
18842
+ const result = [];
18843
+ for (const poi of pois || []) {
18844
+ if (!poi) {
18845
+ continue;
18846
+ }
18847
+ let element;
18848
+ if (poi.elementID) {
18849
+ element = elements.get(poi.elementID);
18850
+ if (!element) {
18851
+ continue; // orphan: the element was deleted (or cannot be read)
18852
+ }
18853
+ }
18854
+ else {
18855
+ // standalone tag: its description is all that identifies it
18856
+ const description = this.stripHtml(poi.tagDescription);
18857
+ element = { name: description, identity: [description] };
18858
+ }
18859
+ const zones = sweepZones.get(poi.matterportSweepID);
18860
+ result.push({
18861
+ poi: {
18862
+ ...poi,
18863
+ elementName: element.name,
18864
+ elementDescription: element.description,
18865
+ elementImageKey: element.imageKey,
18866
+ floorName: zones?.floor,
18867
+ zoneName: zones?.zone
18868
+ },
18869
+ identity: this.identityKey([poi.type, ...element.identity]),
18870
+ // decided by the element's own spaceID rather than by its presence in the
18871
+ // space-wide lists, some of which are not paginated. A standalone tag has no
18872
+ // element to tell: it belongs to every space that shows it.
18873
+ ownedBySpace: !poi.elementID || element.spaceId === spaceId,
18874
+ inVisit: visitSweeps.has(poi.matterportSweepID)
18875
+ });
18876
+ }
18877
+ return result;
18878
+ }
18879
+ /**
18880
+ * Sweeps of a virtual visit (a Node): those of the space's zones attached to that node,
18881
+ * else — zones not filled in for this visit — the sweeps of the node's navigations.
18882
+ */
18883
+ async getSweepsOfVisit(nodeId, zones) {
18884
+ const sweeps = new Set(zones
18885
+ .filter(zone => zone.nodeIDs?.includes(nodeId))
18886
+ .flatMap(zone => zone.sweepIDs || []));
18887
+ if (sweeps.size === 0) {
18888
+ try {
18889
+ const navigations = await this.navigationService.getNavigationsForNode(nodeId);
18890
+ navigations.forEach(navigation => navigation.matterportSweepID && sweeps.add(navigation.matterportSweepID));
18891
+ }
18892
+ catch (error) {
18893
+ console.error('Failed to load the sweeps of the visit:', error);
18894
+ }
18895
+ }
18896
+ return sweeps;
18897
+ }
18898
+ /**
18899
+ * Maps each sweep of a space to the names of its floor (zone of layer FLOOR) and of its
18900
+ * most specific other zone (the one holding the fewest sweeps, typically a room, else the
18901
+ * visit's BUILDING zone).
18902
+ */
18903
+ getSweepZones(zones) {
18904
+ const bySweep = new Map();
18905
+ const isFloor = (zone) => (zone.layer?.name || '').includes('FLOOR');
18906
+ const zoneRank = new Map(); // sweep -> sweep count of the zone kept so far
18907
+ for (const zone of zones) {
18908
+ for (const sweepId of zone.sweepIDs || []) {
18909
+ const entry = bySweep.get(sweepId) || {};
18910
+ if (isFloor(zone)) {
18911
+ entry.floor = entry.floor || zone.name;
18912
+ }
18913
+ else {
18914
+ const size = zone.sweepIDs.length;
18915
+ if (!zoneRank.has(sweepId) || size < zoneRank.get(sweepId)) {
18916
+ zoneRank.set(sweepId, size);
18917
+ entry.zone = zone.name;
18918
+ }
18919
+ }
18920
+ bySweep.set(sweepId, entry);
18921
+ }
18922
+ }
18923
+ return bySweep;
18924
+ }
18925
+ /**
18926
+ * Indexes, by ID, every element of a space a POI can point at. Each list is fetched
18927
+ * independently: one failing (module not enabled, missing permission) must not hide the
18928
+ * others — their POIs are then resolved one by one by fetchTagElementsById().
18929
+ */
18930
+ async getTagElementsForSpace(spaceId) {
18931
+ const elements = new Map();
18932
+ const [tickets, equipments, objects3D, features, comments] = await Promise.allSettled([
18933
+ this.ticketService.getTicketsBySpace(spaceId),
18934
+ this.equipmentService.getEquipmentsForSpace(spaceId),
18935
+ this.object3DService.getObjects3DBySpace(spaceId),
18936
+ this.featureService.getAllFeaturesBySpaceRaw(spaceId),
18937
+ // a measurement POI is bound to the measurement's Comment, not to the measurement
18938
+ this.commentService.getCommentsBySpaceID(spaceId)
18939
+ ]);
18940
+ const index = (settled, type) => {
18941
+ if (settled.status === 'fulfilled') {
18942
+ settled.value?.forEach(item => {
18943
+ if (item?.id) {
18944
+ elements.set(item.id, this.toTagElement(type, { spaceID: spaceId, ...item }));
18945
+ }
18946
+ });
18947
+ }
18948
+ };
18949
+ index(tickets, 'ticket');
18950
+ index(equipments, 'equipment');
18951
+ index(objects3D, 'object3D');
18952
+ index(features, 'feature');
18953
+ index(comments, 'comment');
18954
+ return elements;
18955
+ }
18956
+ /**
18957
+ * Fetches by ID the elements the space-wide lists did not return. The IDs are packed into
18958
+ * a few GraphQL requests using aliases (`e0: getTicket(id: ...) e1: getEquipment(...)`),
18959
+ * instead of one request per element: a space can hold over a thousand orphan POIs.
18960
+ * An element that no longer exists comes back as null and is simply not indexed.
18961
+ */
18962
+ async fetchTagElementsById(pois, elements) {
18963
+ const IDS_PER_REQUEST = 100;
18964
+ const queries = {
18965
+ ticket: 'getTicket(id: $ID) { id spaceID title description }',
18966
+ equipment: 'getEquipment(id: $ID) { id spaceID name serialNumber }',
18967
+ object3D: 'getObject3D(id: $ID) { id spaceID name description }',
18968
+ comment: 'getComment(id: $ID) { id spaceID title description annexe }',
18969
+ feature: 'getFeature(id: $ID) { id spaceID title description }'
18970
+ };
18971
+ const pending = new Map();
18972
+ for (const poi of pois) {
18973
+ pending.set(poi.elementID, this.tagElementTypeOf(poi.type));
18974
+ }
18975
+ const entries = [...pending.entries()];
18976
+ const batches = [];
18977
+ for (let i = 0; i < entries.length; i += IDS_PER_REQUEST) {
18978
+ batches.push(entries.slice(i, i + IDS_PER_REQUEST));
18979
+ }
18980
+ await Promise.all(batches.map(async (batch) => {
18981
+ const fields = batch.map(([id, type], i) => `e${i}: ${queries[type].replace('$ID', JSON.stringify(id))}`);
18982
+ let data;
18983
+ try {
18984
+ data = (await API.graphql(graphqlOperation(`query TagElements { ${fields.join('\n')} }`))).data;
18985
+ }
18986
+ catch (error) {
18987
+ // Amplify rejects when ANY alias errors (e.g. an unreadable record), but still
18988
+ // hands over the data of the aliases that succeeded.
18989
+ data = error?.data;
18990
+ if (!data) {
18991
+ console.error('Failed to fetch tag elements by ID:', error);
18992
+ return;
18993
+ }
18994
+ }
18995
+ batch.forEach(([id, type], i) => {
18996
+ const item = data[`e${i}`];
18997
+ if (item) {
18998
+ elements.set(id, this.toTagElement(type, item));
18999
+ }
19000
+ });
19001
+ }));
19002
+ }
19003
+ /**
19004
+ * Moves a stored coordinate. Only the position and the horizontal orientation change:
19005
+ * every other key is kept as is. A coordinate often carries the tag's orientation next to
19006
+ * its position ({x, y, z, xRotation, yRotation}); rebuilding it from the position alone
19007
+ * silently erased that orientation.
19008
+ */
19009
+ getRecalibratedCoordinate(coordinate, transform) {
19010
+ const position = applyRigidTransformToPoint({ x: coordinate.x, y: coordinate.y, z: coordinate.z }, transform);
19011
+ const moved = { ...coordinate, x: position.x, y: position.y, z: position.z };
19012
+ if ('yRotation' in coordinate) {
19013
+ moved.yRotation = applyRigidTransformToYRotationDeg(coordinate.yRotation, transform);
19014
+ }
19015
+ return moved;
19016
+ }
19017
+ /**
19018
+ * Reads POIs by ID, 100 per GraphQL request (aliases). Missing POIs are skipped. Amplify
19019
+ * rejects a request when ANY alias errors, but still hands over the data of the others:
19020
+ * those are kept, so one unreadable POI does not hide the 99 others of its batch.
19021
+ */
19022
+ async fetchPoisById(ids) {
19023
+ const IDS_PER_REQUEST = 100;
19024
+ const uniqueIds = [...new Set(ids)];
19025
+ const batches = [];
19026
+ for (let i = 0; i < uniqueIds.length; i += IDS_PER_REQUEST) {
19027
+ batches.push(uniqueIds.slice(i, i + IDS_PER_REQUEST));
19028
+ }
19029
+ const results = await Promise.all(batches.map(async (batch) => {
19030
+ const fields = batch.map((id, i) => `p${i}: getPOI(id: ${JSON.stringify(id)}) { id coordinate metadata }`);
19031
+ let data;
19032
+ try {
19033
+ data = (await API.graphql(graphqlOperation(`query SelectedPois { ${fields.join('\n')} }`))).data;
19034
+ }
19035
+ catch (error) {
19036
+ data = error?.data;
19037
+ if (!data) {
19038
+ console.error('Failed to read the selected POIs:', error);
19039
+ return [];
19040
+ }
19041
+ }
19042
+ return batch.map((_, i) => data[`p${i}`]).filter(poi => !!poi);
19043
+ }));
19044
+ return results.flat();
19045
+ }
19046
+ tagElementTypeOf(poiType) {
19047
+ switch (poiType) {
19048
+ case PoiType.TICKET: return 'ticket';
19049
+ case PoiType.EQUIPMENT: return 'equipment';
19050
+ case PoiType.OBJECT3D: return 'object3D';
19051
+ case PoiType.MEASURE: return 'comment';
19052
+ default: return 'feature';
19053
+ }
19054
+ }
19055
+ /** Display name + identity fields of an element, per the copy's duplicate-detection rules. */
19056
+ toTagElement(type, item) {
19057
+ const spaceId = item.spaceID;
19058
+ const text = (value) => this.stripHtml(value) || undefined;
19059
+ switch (type) {
19060
+ case 'equipment':
19061
+ return { name: item.name || '', identity: [item.name, item.serialNumber], spaceId, description: text(item.description) };
19062
+ case 'object3D':
19063
+ return { name: item.name || '', identity: [item.name], spaceId, description: text(item.description) };
19064
+ case 'comment':
19065
+ // A measurement's title is usually empty: its description holds the dimensions and
19066
+ // becomes the name. Its picture is the screenshot taken when it was measured.
19067
+ return {
19068
+ name: item.title || item.description || '',
19069
+ identity: [item.title, item.description],
19070
+ spaceId,
19071
+ description: item.title ? text(item.description) : undefined,
19072
+ imageKey: item.annexe || undefined
19073
+ };
19074
+ default:
19075
+ return { name: item.title || '', identity: [item.title, item.description], spaceId, description: text(item.description) };
19076
+ }
19077
+ }
19078
+ stripHtml(value) {
19079
+ return (value || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
19080
+ }
19081
+ /**
19082
+ * Recalibrates a specific list of existing tags in the target space by applying the rigid
19083
+ * transformation calculated from the selected landmark points.
19084
+ *
19085
+ * Each tag is updated on its own: a failing update is logged and counted, it no longer
19086
+ * stops the remaining tags. Progress and log go through copyProgress$ / copyLog$, like a
19087
+ * copy (phase 'recalibrating'). `tagLabels` (tag id -> name) only makes the log readable.
19088
+ */
19089
+ async recalibrateTagsFromVisit(targetSpaceId, selectedTagIds, landmarkSourceA, landmarkTargetA, landmarkSourceB, landmarkTargetB, tagLabels = {}) {
19090
+ const result = { updated: 0, skipped: 0, failed: 0 };
19091
+ const uniqueIds = [...new Set(selectedTagIds)];
19092
+ const total = uniqueIds.length;
19093
+ const label = (id) => tagLabels[id] || `#${id.substring(0, 8)}`;
19094
+ let processed = 0;
19095
+ const emitProgress = (phase) => {
19096
+ this.copyProgress$.next({
19097
+ phase,
19098
+ processedInPhase: processed, totalInPhase: total,
19099
+ processedOverall: processed, totalOverall: total,
19100
+ percent: total > 0 ? Math.round((processed / total) * 100) : 100,
19101
+ errorCount: result.failed, healedCount: 0
19102
+ });
19103
+ this.setStepLog('recalibrating', this.translate.instant('matterportCopy.recalibrateStep', { processed, total }));
19104
+ };
19105
+ this.resetLog();
19106
+ emitProgress('recalibrating');
19107
+ try {
19108
+ const transform = computeRigidTransform(landmarkSourceA, landmarkTargetA, landmarkSourceB, landmarkTargetB);
19109
+ // Re-read only the selected POIs, by ID, so the transform is applied to their current
19110
+ // position — scanning the whole space again for a handful of tags was the slow path.
19111
+ const targetPois = await this.fetchPoisById(uniqueIds);
19112
+ const foundIds = new Set(targetPois.map(poi => poi.id));
19113
+ uniqueIds.filter(id => !foundIds.has(id)).forEach(id => {
19114
+ result.skipped++;
19115
+ processed++;
19116
+ this.appendErrorLog(this.translate.instant('matterportCopy.recalibrateNotFound', { name: label(id) }));
19117
+ });
19118
+ emitProgress('recalibrating');
19119
+ const UPDATES_IN_PARALLEL = 10;
19120
+ for (let i = 0; i < targetPois.length; i += UPDATES_IN_PARALLEL) {
19121
+ await Promise.all(targetPois.slice(i, i + UPDATES_IN_PARALLEL).map(async (poi) => {
19122
+ const id = poi.id;
19123
+ try {
19124
+ let originalCoord = null;
19125
+ try {
19126
+ originalCoord = typeof poi.coordinate === 'string'
19127
+ ? JSON.parse(poi.coordinate)
19128
+ : poi.coordinate;
19129
+ }
19130
+ catch {
19131
+ originalCoord = null;
19132
+ }
19133
+ if (!originalCoord || typeof originalCoord.x !== 'number') {
19134
+ // virtual POI or unreadable coordinate: no position to move
19135
+ result.skipped++;
19136
+ this.appendErrorLog(this.translate.instant('matterportCopy.recalibrateSkipped', { name: label(id) }));
19137
+ return;
19138
+ }
19139
+ await this.poiService.updatePoi({
19140
+ id,
19141
+ coordinate: JSON.stringify(this.getRecalibratedCoordinate(originalCoord, transform)),
19142
+ metadata: this.getAdjustedPoiMetadata(poi.metadata, transform)
19143
+ });
19144
+ result.updated++;
19145
+ }
19146
+ catch (error) {
19147
+ result.failed++;
19148
+ console.error(`Failed to recalibrate the tag ${id}:`, error);
19149
+ this.appendErrorLog(this.translate.instant('matterportCopy.recalibrateError', { name: label(id), error: toErrorMessage(error) }));
19150
+ }
19151
+ finally {
19152
+ processed++;
19153
+ emitProgress('recalibrating');
19154
+ }
19155
+ }));
19156
+ }
19157
+ }
19158
+ catch (error) {
19159
+ // transform or reading failure: nothing left can be moved
19160
+ console.error('Erreur lors du recalibrage des tags:', error);
19161
+ result.failed = total - result.updated - result.skipped;
19162
+ processed = total;
19163
+ this.appendErrorLog(this.translate.instant('matterportCopy.errorGeneral', { error: toErrorMessage(error) }));
19164
+ }
19165
+ emitProgress('done');
19166
+ this.setStepLog('done', this.translate.instant('matterportCopy.recalibrateSummary', result));
19167
+ return result;
19168
+ }
19169
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportTagCopyService, deps: [{ token: NavigationService }, { token: BaseUserService }, { token: PoiService }, { token: TicketsService }, { token: EquipmentService }, { token: Object3DService }, { token: FeatureService }, { token: MeasurementService }, { token: CommentService }, { token: HashtagService }, { token: MissionService }, { token: OperationService }, { token: DomainService }, { token: i1.TranslateService }, { token: ZoneService }], target: i0.ɵɵFactoryTarget.Injectable });
18549
19170
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportTagCopyService, providedIn: 'root' });
18550
19171
  }
18551
19172
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportTagCopyService, decorators: [{
18552
19173
  type: Injectable,
18553
19174
  args: [{ providedIn: 'root' }]
18554
- }], ctorParameters: () => [{ type: NavigationService }, { type: BaseUserService }, { type: PoiService }, { type: TicketsService }, { type: EquipmentService }, { type: Object3DService }, { type: FeatureService }, { type: MeasurementService }, { type: CommentService }, { type: HashtagService }, { type: MissionService }, { type: OperationService }, { type: DomainService }, { type: i1.TranslateService }] });
19175
+ }], ctorParameters: () => [{ type: NavigationService }, { type: BaseUserService }, { type: PoiService }, { type: TicketsService }, { type: EquipmentService }, { type: Object3DService }, { type: FeatureService }, { type: MeasurementService }, { type: CommentService }, { type: HashtagService }, { type: MissionService }, { type: OperationService }, { type: DomainService }, { type: i1.TranslateService }, { type: ZoneService }] });
18555
19176
 
18556
19177
  /* eslint-disable class-methods-use-this */
18557
19178
  class OrganisationService {