@smarterplan/ngx-smarterplan-core 1.4.17 → 1.4.18

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 (98) 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 +1 -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 +83 -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 +793 -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-tag-copy.service.d.ts +128 -2
  94. package/lib/services/matterport-tag.service.d.ts +24 -1
  95. package/lib/services/matterport.service.d.ts +12 -3
  96. package/lib/services/models/poi.service.d.ts +1 -0
  97. package/package.json +1 -1
  98. 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? }]
@@ -2989,11 +3010,11 @@ class MatterportPointerService {
2989
3010
  }
2990
3011
  updateCursorDisplay(poseMatterport) {
2991
3012
  if (this.textDisplayCursorPositionPanel && poseMatterport) {
2992
- this.textDisplayCursorPositionPanel.innerHTML = `position:
2993
- ${pointToString(poseMatterport.position)}\n
2994
- normal:
2995
- ${pointToString(poseMatterport.normal)}\n
2996
- floorId:
3013
+ this.textDisplayCursorPositionPanel.innerHTML = `position:
3014
+ ${pointToString(poseMatterport.position)}\n
3015
+ normal:
3016
+ ${pointToString(poseMatterport.normal)}\n
3017
+ floorId:
2997
3018
  ${poseMatterport.floorId}`;
2998
3019
  if (this.getCursorPositionButton) {
2999
3020
  this.getCursorPositionButton.style.display = 'none';
@@ -3653,6 +3674,13 @@ class MatterportTagService {
3653
3674
  tagService = null;
3654
3675
  tagsCurrentOpacity = new Map();
3655
3676
  tagsCurrentEnabled = new Map();
3677
+ /** tagId -> icon name last applied, so an unchanged icon is not re-applied. */
3678
+ tagsCurrentIcon = new Map();
3679
+ /** Ids of the global SDK Tag collection, and the tags waiting to appear in it, see waitForTagInSdk(). */
3680
+ sdkTagIds = new Set();
3681
+ sdkTagWaiters = new Map();
3682
+ sdkTagSubscription = null;
3683
+ sdkTagSubscriptionSdk = null;
3656
3684
  /**
3657
3685
  * Single layer holding every Smarterplan tag, added to every view of the space so a
3658
3686
  * tag stays in place — same sid, same content — when the user switches between the
@@ -3696,11 +3724,92 @@ class MatterportTagService {
3696
3724
  * A layer-scoped Tag module only exposes the *replayable* mutations, meant to build
3697
3725
  * the layer's own content; applying them to an already-created tag silently does
3698
3726
  * nothing — which is what left every pin white instead of carrying its icon. Only
3699
- * `add` has to go through the layer, see getTagCreationModule().
3727
+ * `add` has to go through the layer, see getTagCreationModule(). The global module
3728
+ * only knows a layer-created pin once it is rendered: see waitForTagInSdk().
3700
3729
  */
3701
3730
  getTagModule(sdk, tagId) {
3702
3731
  return sdk?.Tag;
3703
3732
  }
3733
+ /** Icon name last applied to a tag, if any. */
3734
+ getCachedTagIcon(tagId) {
3735
+ return this.tagsCurrentIcon.get(tagId);
3736
+ }
3737
+ /** Applies an icon to a tag through its own module and remembers it on success. */
3738
+ async editTagIcon(sdk, tagId, iconName) {
3739
+ await this.getTagModule(sdk, tagId).editIcon(tagId, iconName);
3740
+ this.tagsCurrentIcon.set(tagId, iconName);
3741
+ }
3742
+ /**
3743
+ * Resolves once the global SDK Tag collection — the one edits go through — contains the
3744
+ * tag, so edits do not hit a pin that is not rendered yet ("setPinRenderOverrides on a pin
3745
+ * that doesn't exist"). Gives up after `timeoutMs` and resolves anyway; falls back to a
3746
+ * short wait when the collection cannot be observed. One subscription is shared by every
3747
+ * waiting tag: a space loads hundreds of them.
3748
+ */
3749
+ waitForTagInSdk(sdk, tagId, timeoutMs = 1500) {
3750
+ if (!tagId || !this.observeSdkTags(sdk))
3751
+ return wait(100);
3752
+ if (this.sdkTagIds.has(tagId))
3753
+ return Promise.resolve();
3754
+ return new Promise((resolve) => {
3755
+ const waiters = this.sdkTagWaiters.get(tagId) || [];
3756
+ const timer = setTimeout(() => {
3757
+ const remaining = (this.sdkTagWaiters.get(tagId) || []).filter(waiter => waiter !== done);
3758
+ if (remaining.length)
3759
+ this.sdkTagWaiters.set(tagId, remaining);
3760
+ else
3761
+ this.sdkTagWaiters.delete(tagId);
3762
+ resolve();
3763
+ }, timeoutMs);
3764
+ const done = () => { clearTimeout(timer); resolve(); };
3765
+ waiters.push(done);
3766
+ this.sdkTagWaiters.set(tagId, waiters);
3767
+ });
3768
+ }
3769
+ /** Keeps sdkTagIds in sync with the global SDK Tag collection; false when it cannot be observed. */
3770
+ observeSdkTags(sdk) {
3771
+ if (this.sdkTagSubscription && this.sdkTagSubscriptionSdk === sdk)
3772
+ return true;
3773
+ const data = sdk?.Tag?.data;
3774
+ if (typeof data?.subscribe !== 'function')
3775
+ return false;
3776
+ this.stopObservingSdkTags();
3777
+ const markPresent = (id) => {
3778
+ this.sdkTagIds.add(id);
3779
+ const waiters = this.sdkTagWaiters.get(id);
3780
+ if (waiters) {
3781
+ this.sdkTagWaiters.delete(id);
3782
+ waiters.forEach(waiter => waiter());
3783
+ }
3784
+ };
3785
+ try {
3786
+ this.sdkTagSubscriptionSdk = sdk;
3787
+ this.sdkTagSubscription = data.subscribe({
3788
+ onAdded: (id) => markPresent(id),
3789
+ onRemoved: (id) => this.sdkTagIds.delete(id),
3790
+ onCollectionUpdated: (collection) => {
3791
+ const ids = typeof collection?.keys === 'function'
3792
+ ? Array.from(collection.keys()) : Object.keys(collection || {});
3793
+ this.sdkTagIds = new Set(ids);
3794
+ ids.forEach(id => this.sdkTagWaiters.has(id) && markPresent(id));
3795
+ },
3796
+ });
3797
+ return true;
3798
+ }
3799
+ catch {
3800
+ this.sdkTagSubscriptionSdk = null;
3801
+ return false;
3802
+ }
3803
+ }
3804
+ stopObservingSdkTags() {
3805
+ try {
3806
+ this.sdkTagSubscription?.cancel?.();
3807
+ }
3808
+ catch { }
3809
+ this.sdkTagSubscription = null;
3810
+ this.sdkTagSubscriptionSdk = null;
3811
+ this.sdkTagIds.clear();
3812
+ }
3704
3813
  /**
3705
3814
  * Returns the Tag module every new tag must be created through: the shared layer
3706
3815
  * when the View API is available, the global sdk.Tag otherwise.
@@ -3855,7 +3964,7 @@ class MatterportTagService {
3855
3964
  const [createdSid] = await tagModule.add(...descriptor);
3856
3965
  this.mattertagIDs.push(createdSid);
3857
3966
  this.dictionnaryTags.set(createdSid, mattertagData);
3858
- await wait(100);
3967
+ await this.waitForTagInSdk(sdk, createdSid);
3859
3968
  await setTagIconAndOpacity(createdSid, mattertagData);
3860
3969
  injectHtmlInTag(mattertagData.getType(), mattertagData.getObject(), createdSid).catch(err => {
3861
3970
  if (isDevMode())
@@ -3976,6 +4085,7 @@ class MatterportTagService {
3976
4085
  resetVisibilityCache() {
3977
4086
  this.tagsCurrentOpacity.clear();
3978
4087
  this.tagsCurrentEnabled.clear();
4088
+ this.tagsCurrentIcon.clear();
3979
4089
  }
3980
4090
  /** Removes a specific tag from the viewer and local dictionaries. */
3981
4091
  async deleteMattertagFromId(sdk, mattertagID) {
@@ -3983,6 +4093,9 @@ class MatterportTagService {
3983
4093
  if (sdk)
3984
4094
  await this.getTagModule(sdk, mattertagID).remove(mattertagID);
3985
4095
  this.dictionnaryTags.delete(mattertagID);
4096
+ this.tagsCurrentIcon.delete(mattertagID);
4097
+ this.tagsCurrentOpacity.delete(mattertagID);
4098
+ this.tagsCurrentEnabled.delete(mattertagID);
3986
4099
  const index = this.mattertagIDs.indexOf(mattertagID);
3987
4100
  if (index > -1)
3988
4101
  this.mattertagIDs.splice(index, 1);
@@ -4044,14 +4157,8 @@ class MatterportTagService {
4044
4157
  }
4045
4158
  mattertagData.setPoi(poi);
4046
4159
  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
4160
  if (createdTagID && sdk) {
4054
- await wait(100);
4161
+ await this.waitForTagInSdk(sdk, createdTagID);
4055
4162
  await setTagIconAndOpacity(createdTagID, mattertagData);
4056
4163
  injectHtmlInTag(tagType, object, createdTagID).catch(err => {
4057
4164
  if (isDevMode())
@@ -4080,6 +4187,7 @@ class MatterportTagService {
4080
4187
  const tagModule = this.getTagModule(sdk, mattertagID);
4081
4188
  await sdk.Asset.registerTexture(iconName, iconPath);
4082
4189
  await tagModule.editIcon(mattertagID, iconName);
4190
+ this.tagsCurrentIcon.set(mattertagID, iconName);
4083
4191
  if (applyOpacity) {
4084
4192
  await tagModule.editOpacity(mattertagID, opacity);
4085
4193
  }
@@ -4209,6 +4317,8 @@ class MatterportTagService {
4209
4317
  this.dictionnaryTags.clear();
4210
4318
  this.tagsCurrentOpacity.clear();
4211
4319
  this.tagsCurrentEnabled.clear();
4320
+ this.tagsCurrentIcon.clear();
4321
+ this.stopObservingSdkTags();
4212
4322
  this.tagAddQueue = Promise.resolve();
4213
4323
  this.mattertagToFollow = null;
4214
4324
  this.mattertagIDs = [];
@@ -4332,6 +4442,12 @@ class MatterportService {
4332
4442
  tagAction = new Subject();
4333
4443
  /** Views (furnished, defurnished...) exposed by the SDK for the current space. */
4334
4444
  viewsByType = new Map();
4445
+ /** Settles once every static icon texture has been registered (or failed). */
4446
+ iconsRegistered = Promise.resolve();
4447
+ /** Names of the textures successfully registered in the current SDK session. */
4448
+ registeredIcons = new Set();
4449
+ /** tagId -> signed source last registered as a custom texture, and its texture name. */
4450
+ customIconCache = new Map();
4335
4451
  currentView = null;
4336
4452
  viewSubscriptions = [];
4337
4453
  /** Emits the active view every time the user switches between furnished/defurnished. */
@@ -4460,13 +4576,16 @@ class MatterportService {
4460
4576
  this.addCustomCss();
4461
4577
  this.pointerService.startAdminCursorTracker(this.sdk, this.container, () => this.poseMatterport, () => this.poseCamera);
4462
4578
  // Load Mattertag icons and custom subscriptions
4579
+ this.registeredIcons.clear();
4580
+ this.customIconCache.clear();
4581
+ const iconRegistrations = [];
4463
4582
  switch (module) {
4464
4583
  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);
4584
+ iconRegistrations.push(this.registerIcon('icon-ticket', this.config.my_config.icon_ticket));
4585
+ iconRegistrations.push(this.registerIcon('icon-equipment', this.config.my_config.icon_equipment));
4586
+ iconRegistrations.push(this.registerIcon('icon-measure', this.config.my_config.icon_measure));
4587
+ iconRegistrations.push(this.registerIcon('icon-data', this.config.my_config.icon_data));
4588
+ iconRegistrations.push(this.registerIcon('icon-object3d', this.config.my_config.icon_object3d));
4470
4589
  this.sdk.Measurements.data.subscribe({
4471
4590
  onAdded: function (index, item, collection) {
4472
4591
  this.measurementService.setLastMeasure(item.points);
@@ -4483,20 +4602,19 @@ class MatterportService {
4483
4602
  }.bind(this));
4484
4603
  break;
4485
4604
  case SpModule.MUSEUM:
4486
- this.sdk.Asset.registerTexture('icon-data', this.config.my_config.icon_data);
4605
+ iconRegistrations.push(this.registerIcon('icon-data', this.config.my_config.icon_data));
4487
4606
  break;
4488
4607
  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);
4608
+ iconRegistrations.push(this.registerIcon('icon_room_available', this.config.my_config.icon_room_available));
4609
+ iconRegistrations.push(this.registerIcon('icon_room_unavailable', this.config.my_config.icon_room_unavailable));
4610
+ iconRegistrations.push(this.registerIcon('icon_room_chosen', this.config.my_config.icon_room_chosen));
4611
+ iconRegistrations.push(this.registerIcon('icon_room_viewed', this.config.my_config.icon_room_viewed));
4493
4612
  break;
4494
4613
  default:
4495
4614
  break;
4496
4615
  }
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); });
4616
+ iconRegistrations.push(this.registerIcon('icon-position', this.config.my_config.icon_position));
4617
+ this.iconsRegistered = Promise.allSettled(iconRegistrations).then(() => undefined);
4500
4618
  // Current Room (used for getting bounding box and eventually lazy load mattertag or object3D inside current Room)
4501
4619
  this.sdk.Room.current.subscribe((currentRoom) => {
4502
4620
  const rooms = Array.isArray(currentRoom) ? currentRoom : (currentRoom ? [currentRoom] : []);
@@ -4854,15 +4972,29 @@ class MatterportService {
4854
4972
  /** Applies a pre-registered icon to a Mattertag. */
4855
4973
  async setRegistredIconForTag(mattertagID, iconName) {
4856
4974
  try {
4857
- this.matterportTagService.getTagModule(this.sdk, mattertagID).editIcon(mattertagID, iconName);
4975
+ await this.matterportTagService.editTagIcon(this.sdk, mattertagID, iconName);
4858
4976
  }
4859
4977
  catch (e) {
4860
4978
  if (isDevMode())
4861
4979
  console.warn('Could not edit icon', iconName, e);
4862
4980
  }
4863
4981
  }
4864
- /** Sets the appropriate icon and opacity for a tag based on its type and POI metadata. */
4982
+ /** Registers a static texture and remembers its name once the SDK accepted it. */
4983
+ async registerIcon(iconName, iconPath) {
4984
+ if (!iconPath)
4985
+ return;
4986
+ try {
4987
+ await this.sdk.Asset.registerTexture(iconName, iconPath);
4988
+ this.registeredIcons.add(iconName);
4989
+ }
4990
+ catch (e) {
4991
+ if (isDevMode())
4992
+ console.warn(`[MatterportService] ${iconName} texture registration failed`, e);
4993
+ }
4994
+ }
4995
+ /** Sets the appropriate icon for a tag based on its type and POI metadata. */
4865
4996
  async setTagIconAndOpacity(mattertagID, mattertagData) {
4997
+ await this.iconsRegistered;
4866
4998
  if (this.SPModule === SpModule.HOTEL) {
4867
4999
  const room = mattertagData.getObject();
4868
5000
  let iconName = `icon_room`;
@@ -4876,7 +5008,6 @@ class MatterportService {
4876
5008
  return;
4877
5009
  }
4878
5010
  const stringTagType = poiTypeToString(mattertagData.getType());
4879
- let opacity = this.config.my_config.DEFAULT_OPACITY_TAG;
4880
5011
  let iconName = `icon-${stringTagType}`;
4881
5012
  const poi = mattertagData.getPoi();
4882
5013
  if (poi && poi.tagIcon) {
@@ -4888,31 +5019,45 @@ class MatterportService {
4888
5019
  }
4889
5020
  }
4890
5021
  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');
5022
+ // keyed on the stored source, not on the signed URL: a new signature every call
5023
+ const cached = this.customIconCache.get(mattertagID);
5024
+ if (cached && cached.src === tagIcon.src) {
5025
+ iconName = cached.iconName;
5026
+ }
5027
+ else {
5028
+ const pathSigned = await this.tagService.getSignedTagIconSource(tagIcon.src);
5029
+ if (pathSigned) {
5030
+ try {
5031
+ const customIconName = `icon-${stringTagType}-${mattertagID}-${mattertagData.customIconIndex}`;
5032
+ await this.sdk.Asset.registerTexture(customIconName, pathSigned);
5033
+ mattertagData.customIconIndex += 1;
5034
+ this.customIconCache.set(mattertagID, { src: tagIcon.src, iconName: customIconName });
5035
+ this.registeredIcons.add(customIconName);
5036
+ iconName = customIconName;
5037
+ }
5038
+ catch {
5039
+ console.log('error registerIcon');
5040
+ }
4900
5041
  }
4901
5042
  }
4902
5043
  }
4903
- if (tagIcon.opacity) {
4904
- opacity = tagIcon.opacity;
4905
- }
4906
5044
  }
4907
5045
  await this.editTagIcon(mattertagID, iconName);
4908
5046
  }
4909
5047
  /**
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.
5048
+ * Applies an icon to a tag, unless that icon is already the one applied. The call is
5049
+ * awaited and its rejection reported: a silently dropped promise here leaves the pin
5050
+ * with its default white look.
4912
5051
  */
4913
5052
  async editTagIcon(mattertagID, iconName) {
5053
+ if (this.matterportTagService.getCachedTagIcon(mattertagID) === iconName)
5054
+ return;
5055
+ // A texture that was never registered (no icon configured for this type) can only fail:
5056
+ // the pin keeps its default look, as before, without a doomed SDK call.
5057
+ if (!this.registeredIcons.has(iconName))
5058
+ return;
4914
5059
  try {
4915
- await this.matterportTagService.getTagModule(this.sdk, mattertagID).editIcon(mattertagID, iconName);
5060
+ await this.matterportTagService.editTagIcon(this.sdk, mattertagID, iconName);
4916
5061
  }
4917
5062
  catch (error) {
4918
5063
  if (isDevMode())
@@ -5015,22 +5160,22 @@ class MatterportService {
5015
5160
  );
5016
5161
  }*/
5017
5162
  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
- }
5163
+ const css = `
5164
+ .footer-ui,
5165
+ .showcase-footer,
5166
+ .mp-nova-btn-group,
5167
+ .footer-divider,
5168
+ .logo-link,
5169
+ .footer-logo,
5170
+ .bottom-ui,
5171
+ img.logo-image,
5172
+ img[class^="logo-image"],
5173
+ img[class*=" logo-image"],
5174
+ [data-testid="showcase-footer"] {
5175
+ opacity: 0 !important;
5176
+ visibility: hidden !important;
5177
+ pointer-events: none !important;
5178
+ }
5034
5179
  `;
5035
5180
  // 1st method: Via Scene SDK
5036
5181
  if (this.sdk?.Scene?.addStyle) {
@@ -8046,11 +8191,11 @@ class ModalSwitchVisitComponent {
8046
8191
  });
8047
8192
  }
8048
8193
  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" }] });
8194
+ 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
8195
  }
8051
8196
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ModalSwitchVisitComponent, decorators: [{
8052
8197
  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"] }]
8198
+ 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
8199
  }], ctorParameters: () => [{ type: i1$2.NgbActiveModal }, { type: i1$1.Router }, { type: ViewerService }], propDecorators: { content: [{
8055
8200
  type: Input
8056
8201
  }], model3D: [{
@@ -8587,14 +8732,18 @@ class ZoneService {
8587
8732
  async getZonesBySpace(spaceID) {
8588
8733
  const list = [];
8589
8734
  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) {
8735
+ // Paginated: without a nextToken loop, only the first page (100 zones) was returned and
8736
+ // the sweeps of the other zones silently disappeared.
8737
+ let nextToken = null;
8738
+ do {
8739
+ const zones = await this.API.__proto__.ZonesBySpace(spaceID, null, null, 1000, nextToken);
8740
+ for (const zone of zones?.items || []) {
8593
8741
  const zoneObject = { ...zone };
8594
8742
  zoneObject.virtual = this.zoneIsVirtual(zone);
8595
8743
  list.push(zoneObject);
8596
8744
  }
8597
- }
8745
+ nextToken = zones?.nextToken ?? null;
8746
+ } while (nextToken);
8598
8747
  return list;
8599
8748
  }
8600
8749
  else {
@@ -13064,11 +13213,11 @@ class SearchBarComponent {
13064
13213
  this.searchEvent.emit(results);
13065
13214
  }
13066
13215
  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" }] });
13216
+ 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
13217
  }
13069
13218
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SearchBarComponent, decorators: [{
13070
13219
  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"] }]
13220
+ 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
13221
  }], ctorParameters: () => [{ type: SearchService }], propDecorators: { searchable: [{
13073
13222
  type: Input
13074
13223
  }], objectType: [{
@@ -13516,11 +13665,11 @@ class LoaderComponent {
13516
13665
  document.documentElement.style.setProperty('--color', this.color);
13517
13666
  }
13518
13667
  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"] }] });
13668
+ 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
13669
  }
13521
13670
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: LoaderComponent, decorators: [{
13522
13671
  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"] }]
13672
+ 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
13673
  }], ctorParameters: () => [], propDecorators: { useLogo: [{
13525
13674
  type: Input
13526
13675
  }], color: [{
@@ -13578,11 +13727,11 @@ class SupportModalComponent {
13578
13727
  }
13579
13728
  }
13580
13729
  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" }] });
13730
+ 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
13731
  }
13583
13732
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SupportModalComponent, decorators: [{
13584
13733
  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"] }]
13734
+ 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
13735
  }], ctorParameters: () => [{ type: SupportService }, { type: i1$2.NgbActiveModal }, { type: i3.UntypedFormBuilder }, { type: BaseUserService }, { type: ValidatorsService }] });
13587
13736
 
13588
13737
  class LocaleService {
@@ -14993,11 +15142,11 @@ class NavigationBarComponent {
14993
15142
  this.baseVisibilityService.isChangePositionVisible.next(false);
14994
15143
  }
14995
15144
  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" }] });
15145
+ 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
15146
  }
14998
15147
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NavigationBarComponent, decorators: [{
14999
15148
  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"] }]
15149
+ 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
15150
  }], ctorParameters: () => [{ type: undefined, decorators: [{
15002
15151
  type: Inject,
15003
15152
  args: ['currentAwsKinesisAnalyticsService']
@@ -15266,11 +15415,11 @@ class MenuBarComponent {
15266
15415
  }
15267
15416
  }
15268
15417
  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" }] });
15418
+ 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
15419
  }
15271
15420
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MenuBarComponent, decorators: [{
15272
15421
  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"] }]
15422
+ 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
15423
  }], 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
15424
  type: Input
15276
15425
  }] } });
@@ -15425,27 +15574,27 @@ class BaseTagService {
15425
15574
  throw new Error('updatePoiMetadata is not implemented in the base class.');
15426
15575
  }*/
15427
15576
  async prepareEquipmentHtml(equip) {
15428
- throw new Error(`prepareEquipmentHtml is not implemented in the base class.
15577
+ throw new Error(`prepareEquipmentHtml is not implemented in the base class.
15429
15578
  Please override this method in your extended class.`);
15430
15579
  }
15431
15580
  async prepareTicketHtml(ticket) {
15432
- throw new Error(`prepareTicketHtml is not implemented in the base class.
15581
+ throw new Error(`prepareTicketHtml is not implemented in the base class.
15433
15582
  Please override this method in your extended class.`);
15434
15583
  }
15435
15584
  async prepareFeatureHtml(feature) {
15436
- throw new Error(`prepareFeatureHtml is not implemented in the base class.
15585
+ throw new Error(`prepareFeatureHtml is not implemented in the base class.
15437
15586
  Please override this method in your extended class.`);
15438
15587
  }
15439
15588
  async prepareMeasurementHtml(measure) {
15440
- throw new Error(`prepareMeasurementHtml is not implemented in the base class.
15589
+ throw new Error(`prepareMeasurementHtml is not implemented in the base class.
15441
15590
  Please override this method in your extended class.`);
15442
15591
  }
15443
15592
  async prepareDeskHtml(feature) {
15444
- throw new Error(`prepareDeskHtml is not implemented in the base class.
15593
+ throw new Error(`prepareDeskHtml is not implemented in the base class.
15445
15594
  Please override this method in your extended class.`);
15446
15595
  }
15447
15596
  async prepareIndicatorHtml(feature) {
15448
- throw new Error(`prepareDeskHtml is not implemented in the base class.
15597
+ throw new Error(`prepareDeskHtml is not implemented in the base class.
15449
15598
  Please override this method in your extended class.`);
15450
15599
  }
15451
15600
  async getSignedTagIconSource(tagIconSrc) {
@@ -15454,15 +15603,15 @@ class BaseTagService {
15454
15603
  getIconTagImageForFeature(feature, poi) {
15455
15604
  // const tagIcon = JSON.parse(poi.tagIcon);
15456
15605
  // return tagIcon.src;
15457
- throw new Error(`getIconTagImageForFeature is not implemented in the base class.
15606
+ throw new Error(`getIconTagImageForFeature is not implemented in the base class.
15458
15607
  Please override this method in your extended class.`);
15459
15608
  }
15460
15609
  getScriptForTag(object, tagType) {
15461
- throw new Error(`getScriptForTag is not implemented in the base class.
15610
+ throw new Error(`getScriptForTag is not implemented in the base class.
15462
15611
  Please override this method in your extended class.`);
15463
15612
  }
15464
15613
  getAnnexeForCommentTypeInFeature(feature, commentType) {
15465
- throw new Error(`getAnnexeForCommentTypeInFeature is not implemented in the base class.
15614
+ throw new Error(`getAnnexeForCommentTypeInFeature is not implemented in the base class.
15466
15615
  Please override this method in your extended class.`);
15467
15616
  }
15468
15617
  getBillboardMediaToEmbed(object) {
@@ -15471,27 +15620,27 @@ class BaseTagService {
15471
15620
  return { comment: null, tagDescription: '' };
15472
15621
  }
15473
15622
  onActionDetailClick(url) {
15474
- throw new Error(`onActionDetailClick is not implemented in the base class.
15623
+ throw new Error(`onActionDetailClick is not implemented in the base class.
15475
15624
  Please override this method in your extended class.`);
15476
15625
  }
15477
15626
  onActionAudioClick(audioCommentID) {
15478
- throw new Error(`onActionDetailClick is not implemented in the base class.
15627
+ throw new Error(`onActionDetailClick is not implemented in the base class.
15479
15628
  Please override this method in your extended class.`);
15480
15629
  }
15481
15630
  onActionVideoClick(url) {
15482
- throw new Error(`onActionVideoClick is not implemented in the base class.
15631
+ throw new Error(`onActionVideoClick is not implemented in the base class.
15483
15632
  Please override this method in your extended class.`);
15484
15633
  }
15485
15634
  onActionImageClick(imageCommentID) {
15486
- throw new Error(`onActionImageClick is not implemented in the base class.
15635
+ throw new Error(`onActionImageClick is not implemented in the base class.
15487
15636
  Please override this method in your extended class.`);
15488
15637
  }
15489
15638
  onActionDocClick(url) {
15490
- throw new Error(`onActionDocClick is not implemented in the base class.
15639
+ throw new Error(`onActionDocClick is not implemented in the base class.
15491
15640
  Please override this method in your extended class.`);
15492
15641
  }
15493
15642
  onActionYoutubeClick(url) {
15494
- throw new Error(`onActionYoutubeClick is not implemented in the base class.
15643
+ throw new Error(`onActionYoutubeClick is not implemented in the base class.
15495
15644
  Please override this method in your extended class.`);
15496
15645
  }
15497
15646
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BaseTagService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -15599,6 +15748,11 @@ class MatterportImportService {
15599
15748
  }
15600
15749
  async initSdkForModel(modelID) {
15601
15750
  this.stop = false;
15751
+ // This service is a singleton: without a reset, the readiness wait below was satisfied at
15752
+ // once by the sweeps/floors of the PREVIOUS import, and the new import ran on stale data.
15753
+ this.sweeps = undefined;
15754
+ this.floors = undefined;
15755
+ this.currentFloor = undefined;
15602
15756
  // Load SDK
15603
15757
  console.log('Loading Matterport SDK');
15604
15758
  this.modelID = modelID;
@@ -15636,8 +15790,9 @@ class MatterportImportService {
15636
15790
  }.bind(this),
15637
15791
  });
15638
15792
  await this.sdk.Settings.update('labels', false);
15793
+ // The showcase can take well over 7 s to publish its sweeps on a slow connection.
15639
15794
  let timeoutExpired = false;
15640
- setTimeout(() => (timeoutExpired = true), 7000);
15795
+ setTimeout(() => (timeoutExpired = true), 20000);
15641
15796
  await waitUntil(() => (this.sweeps !== undefined && this.floors !== undefined) ||
15642
15797
  timeoutExpired === true);
15643
15798
  if (timeoutExpired) {
@@ -15684,7 +15839,8 @@ class MatterportImportService {
15684
15839
  const layerBuilding = await this.getLayerByName('BUILDING');
15685
15840
  const createdDefaultZone = await this.createZone(node.metadata, layerBuilding.id, surface, mainZone.id, Object.keys(this.sweeps), node);
15686
15841
  if (!createdDefaultZone) {
15687
- return Promise.resolve(false);
15842
+ // Nothing else can be imported without this zone: fail loudly instead of "succeeding"
15843
+ return Promise.reject(new Error('the zone of the visit could not be created'));
15688
15844
  }
15689
15845
  if (this.floors) {
15690
15846
  floorZones = await this.importFloorData(createdDefaultZone.id, node);
@@ -16736,6 +16892,7 @@ class MatterportTagCopyService {
16736
16892
  operationService;
16737
16893
  domainService;
16738
16894
  translate;
16895
+ zoneService;
16739
16896
  // Emits progress updates during copyTagsFromVisit(); null when idle
16740
16897
  copyProgress$ = new BehaviorSubject(null);
16741
16898
  // Emits the accumulated log lines during copyTagsFromVisit(); [] when idle
@@ -16795,7 +16952,7 @@ class MatterportTagCopyService {
16795
16952
  targetDomainIdsByTypeAndName = new Map();
16796
16953
  /** Target space id of the run in progress — used to re-home copied tag icons. */
16797
16954
  targetSpaceId = null;
16798
- constructor(navigationService, userService, poiService, ticketService, equipmentService, object3DService, featureService, measurementService, commentService, hashtagService, missionService, operationService, domainService, translate) {
16955
+ constructor(navigationService, userService, poiService, ticketService, equipmentService, object3DService, featureService, measurementService, commentService, hashtagService, missionService, operationService, domainService, translate, zoneService) {
16799
16956
  this.navigationService = navigationService;
16800
16957
  this.userService = userService;
16801
16958
  this.poiService = poiService;
@@ -16810,6 +16967,7 @@ class MatterportTagCopyService {
16810
16967
  this.operationService = operationService;
16811
16968
  this.domainService = domainService;
16812
16969
  this.translate = translate;
16970
+ this.zoneService = zoneService;
16813
16971
  this.spModule = userService.getSpModule();
16814
16972
  }
16815
16973
  //
@@ -16818,6 +16976,8 @@ class MatterportTagCopyService {
16818
16976
  async copyTagsFromVisit(sourceSpaceId, targetSpaceId, landmarkSourceA, landmarkTargetA, landmarkSourceB, landmarkTargetB) {
16819
16977
  let errorCount = 0;
16820
16978
  let healedCount = 0;
16979
+ // items skipped because an equivalent already exists in the target (earlier run)
16980
+ let alreadyPresentCount = 0;
16821
16981
  this.resetLog();
16822
16982
  this.resetRunState(targetSpaceId);
16823
16983
  try {
@@ -16886,8 +17046,9 @@ class MatterportTagCopyService {
16886
17046
  // loadDomainIndex() for why this is keyed to Mission, not to the Space itself.
16887
17047
  await this.loadDomainIndex(sourceSpaceId, targetSpaceId);
16888
17048
  if (this.spModule == SpModule.IMMO) {
16889
- sourceMeasurements = await this.measurementService.getMeasurementsForSpaceForUser(sourceSpaceId);
16890
- targetMeasurements = await this.measurementService.getMeasurementsForSpaceForUser(targetSpaceId);
17049
+ sourceMeasurements = (await this.measurementService.getMeasurementsForSpaceForUser(sourceSpaceId))
17050
+ .filter(m => (m.numberPoints ?? 0) > 0);
17051
+ targetMeasurements = (await this.measurementService.getMeasurementsForSpaceForUser(targetSpaceId));
16891
17052
  }
16892
17053
  this.setStepProgress('collecting', 'collecting', 90);
16893
17054
  const sourcePoisAll = await this.poiService.getPoisByMatterportSweeps(sourceSweepIds);
@@ -16951,6 +17112,9 @@ class MatterportTagCopyService {
16951
17112
  const sourceObj = sourceTickets[i];
16952
17113
  try {
16953
17114
  const existingMatch = this.findExisting(sourceObj, targetTickets, (t) => [t.title, t.description]);
17115
+ if (existingMatch) {
17116
+ alreadyPresentCount++;
17117
+ }
16954
17118
  if (!existingMatch) {
16955
17119
  const rawSource = sourceObj;
16956
17120
  const ticketInput = {
@@ -17070,6 +17234,9 @@ class MatterportTagCopyService {
17070
17234
  // Equipment has no `title`/`description`: it identifies itself by name, and by
17071
17235
  // serial number when one is recorded.
17072
17236
  const existingMatch = this.findExisting(sourceObj, targetEquipments, (e) => [e.name, e.serialNumber]);
17237
+ if (existingMatch) {
17238
+ alreadyPresentCount++;
17239
+ }
17073
17240
  if (!existingMatch) {
17074
17241
  const rawSource = sourceObj;
17075
17242
  let copiedPhotoKey = null;
@@ -17249,6 +17416,9 @@ class MatterportTagCopyService {
17249
17416
  const sourceObj = sourceObjects3D[i];
17250
17417
  try {
17251
17418
  const existsInTarget = targetObjects3D.some(targetObj => targetObj.name === sourceObj.name);
17419
+ if (existsInTarget) {
17420
+ alreadyPresentCount++;
17421
+ }
17252
17422
  if (!existsInTarget) {
17253
17423
  const rawSource = sourceObj;
17254
17424
  // `path` means two different things depending on `s3Bucket`, exactly as the
@@ -17366,6 +17536,9 @@ class MatterportTagCopyService {
17366
17536
  const sourceObj = sourceFeatures[i];
17367
17537
  try {
17368
17538
  const existingMatch = this.findExisting(sourceObj, targetFeatures, (f) => [f.title, f.description]);
17539
+ if (existingMatch) {
17540
+ alreadyPresentCount++;
17541
+ }
17369
17542
  if (!existingMatch) {
17370
17543
  const rawSource = sourceObj;
17371
17544
  // Resolved just before the write: a keyword is only created in the target
@@ -17470,6 +17643,9 @@ class MatterportTagCopyService {
17470
17643
  const sourceMeasurement = sourceMeasurements[i];
17471
17644
  try {
17472
17645
  const existsInTarget = targetMeasurements.some(tm => tm.comment.title === sourceMeasurement.comment.title);
17646
+ if (existsInTarget) {
17647
+ alreadyPresentCount++;
17648
+ }
17473
17649
  if (!existsInTarget) {
17474
17650
  const rawComment = sourceMeasurement.comment;
17475
17651
  let copiedAnnexe = null;
@@ -17529,7 +17705,15 @@ class MatterportTagCopyService {
17529
17705
  for (let i = 0; i < sourcePois.length; i++) {
17530
17706
  const sourcePoi = sourcePois[i];
17531
17707
  try {
17532
- const existsInTarget = targetPois.some(tp => tp.coordinate === sourcePoi.coordinate && tp.tagDescription === sourcePoi.tagDescription);
17708
+ // Compared on the parsed position, where the copy WOULD land: the stored strings
17709
+ // differ in format ('{ "x":' vs '{"x":') and, once landmarks move the tags, the
17710
+ // source string never equals its copy's, so every re-run duplicated these POIs.
17711
+ const expectedPosition = this.parseCoordinate(this.getAdjustedPositionOnly(sourcePoi.coordinate, transform));
17712
+ const existsInTarget = targetPois.some(tp => tp.tagDescription === sourcePoi.tagDescription
17713
+ && this.isSamePosition(this.parseCoordinate(tp.coordinate), expectedPosition));
17714
+ if (existsInTarget) {
17715
+ alreadyPresentCount++;
17716
+ }
17533
17717
  if (!existsInTarget) {
17534
17718
  const elementID = sourcePoi.elementID
17535
17719
  ? (elementIds[sourcePoi.elementID] || sourcePoi.elementID)
@@ -17564,6 +17748,10 @@ class MatterportTagCopyService {
17564
17748
  if (healedCount > 0) {
17565
17749
  this.setStepLog('healedSummary', this.translate.instant('matterportCopy.healedSummary', { count: healedCount }));
17566
17750
  }
17751
+ // Says plainly that a re-run created nothing new, instead of a bare "success".
17752
+ if (alreadyPresentCount > 0) {
17753
+ this.setStepLog('alreadyPresentSummary', this.translate.instant('matterportCopy.alreadyPresentSummary', { count: alreadyPresentCount }));
17754
+ }
17567
17755
  return true;
17568
17756
  }
17569
17757
  catch (error) {
@@ -18320,6 +18508,28 @@ class MatterportTagCopyService {
18320
18508
  yRotation: applyRigidTransformToYRotationDeg(parsed.yRotation, transform)
18321
18509
  };
18322
18510
  }
18511
+ /**
18512
+ * Reads a stored coordinate (JSON string or object, whatever its spacing) as a position.
18513
+ * Null for a virtual POI or an unreadable value.
18514
+ */
18515
+ parseCoordinate(value) {
18516
+ try {
18517
+ const parsed = typeof value === 'string' ? JSON.parse(value) : value;
18518
+ return parsed && typeof parsed.x === 'number'
18519
+ ? { x: parsed.x, y: parsed.y, z: parsed.z }
18520
+ : null;
18521
+ }
18522
+ catch {
18523
+ return null;
18524
+ }
18525
+ }
18526
+ /** Two positions within 1 cm are the same (two virtual POIs, both null, are too). */
18527
+ isSamePosition(a, b, toleranceM = 0.01) {
18528
+ if (!a || !b) {
18529
+ return !a && !b;
18530
+ }
18531
+ return distance(a, b) <= toleranceM;
18532
+ }
18323
18533
  /**
18324
18534
  * Applies the rigid transform's translation+rotation to a plain {x,y,z} position,
18325
18535
  * WITHOUT adding xRotation/yRotation keys. Used for measurements, whose stored
@@ -18545,13 +18755,403 @@ class MatterportTagCopyService {
18545
18755
  // for the CopySource header. Do NOT re-encode it here.
18546
18756
  return extracted;
18547
18757
  }
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 });
18758
+ /**
18759
+ * Both tag lists a recalibration can offer for a target space, computed in one pass. Only
18760
+ * the target location's own tags are ever listed (see RecalibrationTagScope); tags whose
18761
+ * element was deleted — orphan POIs — never are.
18762
+ * - `space`: every tag of the location, in any of its visits;
18763
+ * - `visit`: the tags of the location sitting on the sweeps of the selected target visit
18764
+ * (`options.targetNodeId`); empty when no visit is given.
18765
+ * With `options.onlyTagsInSource` and a source other than the target, both lists only keep
18766
+ * the tags that also exist in the source location (same identity as the copy's duplicate
18767
+ * detection): typically the tags an earlier copy brought over.
18768
+ *
18769
+ * A POI carries no name of its own: each tag gets `elementName`, the name of the element
18770
+ * it points at (ticket title, equipment name, ...).
18771
+ */
18772
+ async getRecalibrationTagSets(sourceSpaceId, targetSpaceId, options = {}) {
18773
+ if (!sourceSpaceId || !targetSpaceId) {
18774
+ return { space: [], visit: [] };
18775
+ }
18776
+ const filterBySource = !!options.onlyTagsInSource && sourceSpaceId !== targetSpaceId;
18777
+ const [targetTags, sourceTags] = await Promise.all([
18778
+ this.loadLiveTagsForSpace(targetSpaceId, options.targetNodeId),
18779
+ filterBySource ? this.loadLiveTagsForSpace(sourceSpaceId) : Promise.resolve(null)
18780
+ ]);
18781
+ const sourceKeys = sourceTags && new Set(sourceTags.filter(tag => tag.ownedBySpace).map(tag => tag.identity));
18782
+ const ownTargetTags = targetTags
18783
+ .filter(tag => tag.ownedBySpace)
18784
+ .filter(tag => !sourceKeys || sourceKeys.has(tag.identity));
18785
+ return {
18786
+ space: ownTargetTags.map(tag => tag.poi),
18787
+ visit: ownTargetTags.filter(tag => tag.inVisit).map(tag => tag.poi)
18788
+ };
18789
+ }
18790
+ /**
18791
+ * Signed, displayable URL of a tag's picture (`elementImageKey`). Meant to be called on
18792
+ * demand — when the tag is previewed — rather than for the whole list.
18793
+ */
18794
+ async getTagImageUrl(imageKey) {
18795
+ const key = this.extractS3KeyFromValue(imageKey);
18796
+ return key ? getSignedFile(key) : null;
18797
+ }
18798
+ /**
18799
+ * Loads the POIs placed on the sweeps of a space whose element still exists, each with a
18800
+ * display name, an identity key and whether its element belongs to that space. The key
18801
+ * follows the same rules as the copy's duplicate detection (findExisting), so a tag and its
18802
+ * copy in another space share the same key. With a visit node, `inVisit` tells whether the
18803
+ * tag sits on one of that visit's sweeps.
18804
+ */
18805
+ async loadLiveTagsForSpace(spaceId, visitNodeId) {
18806
+ const [pois, elements, zones] = await Promise.all([
18807
+ this.navigationService.getAllowedSweepsIDsForUserInSpace(spaceId)
18808
+ .then(sweepIds => this.poiService.getPoisByMatterportSweeps(sweepIds)),
18809
+ this.getTagElementsForSpace(spaceId),
18810
+ this.zoneService.getZonesBySpace(spaceId).catch(error => {
18811
+ console.error('Failed to load the zones of the space:', error);
18812
+ return [];
18813
+ })
18814
+ ]);
18815
+ const sweepZones = this.getSweepZones(zones || []);
18816
+ const visitSweeps = visitNodeId ? await this.getSweepsOfVisit(visitNodeId, zones || []) : new Set();
18817
+ // A POI can point at an element stored under another space (left behind by an earlier
18818
+ // copy): those are not in the space-wide lists and are fetched by ID.
18819
+ const unresolved = (pois || []).filter(poi => poi?.elementID && !elements.has(poi.elementID));
18820
+ await this.fetchTagElementsById(unresolved, elements);
18821
+ const result = [];
18822
+ for (const poi of pois || []) {
18823
+ if (!poi) {
18824
+ continue;
18825
+ }
18826
+ let element;
18827
+ if (poi.elementID) {
18828
+ element = elements.get(poi.elementID);
18829
+ if (!element) {
18830
+ continue; // orphan: the element was deleted (or cannot be read)
18831
+ }
18832
+ }
18833
+ else {
18834
+ // standalone tag: its description is all that identifies it
18835
+ const description = this.stripHtml(poi.tagDescription);
18836
+ element = { name: description, identity: [description] };
18837
+ }
18838
+ const zones = sweepZones.get(poi.matterportSweepID);
18839
+ result.push({
18840
+ poi: {
18841
+ ...poi,
18842
+ elementName: element.name,
18843
+ elementDescription: element.description,
18844
+ elementImageKey: element.imageKey,
18845
+ floorName: zones?.floor,
18846
+ zoneName: zones?.zone
18847
+ },
18848
+ identity: this.identityKey([poi.type, ...element.identity]),
18849
+ // decided by the element's own spaceID rather than by its presence in the
18850
+ // space-wide lists, some of which are not paginated. A standalone tag has no
18851
+ // element to tell: it belongs to every space that shows it.
18852
+ ownedBySpace: !poi.elementID || element.spaceId === spaceId,
18853
+ inVisit: visitSweeps.has(poi.matterportSweepID)
18854
+ });
18855
+ }
18856
+ return result;
18857
+ }
18858
+ /**
18859
+ * Sweeps of a virtual visit (a Node): those of the space's zones attached to that node,
18860
+ * else — zones not filled in for this visit — the sweeps of the node's navigations.
18861
+ */
18862
+ async getSweepsOfVisit(nodeId, zones) {
18863
+ const sweeps = new Set(zones
18864
+ .filter(zone => zone.nodeIDs?.includes(nodeId))
18865
+ .flatMap(zone => zone.sweepIDs || []));
18866
+ if (sweeps.size === 0) {
18867
+ try {
18868
+ const navigations = await this.navigationService.getNavigationsForNode(nodeId);
18869
+ navigations.forEach(navigation => navigation.matterportSweepID && sweeps.add(navigation.matterportSweepID));
18870
+ }
18871
+ catch (error) {
18872
+ console.error('Failed to load the sweeps of the visit:', error);
18873
+ }
18874
+ }
18875
+ return sweeps;
18876
+ }
18877
+ /**
18878
+ * Maps each sweep of a space to the names of its floor (zone of layer FLOOR) and of its
18879
+ * most specific other zone (the one holding the fewest sweeps, typically a room, else the
18880
+ * visit's BUILDING zone).
18881
+ */
18882
+ getSweepZones(zones) {
18883
+ const bySweep = new Map();
18884
+ const isFloor = (zone) => (zone.layer?.name || '').includes('FLOOR');
18885
+ const zoneRank = new Map(); // sweep -> sweep count of the zone kept so far
18886
+ for (const zone of zones) {
18887
+ for (const sweepId of zone.sweepIDs || []) {
18888
+ const entry = bySweep.get(sweepId) || {};
18889
+ if (isFloor(zone)) {
18890
+ entry.floor = entry.floor || zone.name;
18891
+ }
18892
+ else {
18893
+ const size = zone.sweepIDs.length;
18894
+ if (!zoneRank.has(sweepId) || size < zoneRank.get(sweepId)) {
18895
+ zoneRank.set(sweepId, size);
18896
+ entry.zone = zone.name;
18897
+ }
18898
+ }
18899
+ bySweep.set(sweepId, entry);
18900
+ }
18901
+ }
18902
+ return bySweep;
18903
+ }
18904
+ /**
18905
+ * Indexes, by ID, every element of a space a POI can point at. Each list is fetched
18906
+ * independently: one failing (module not enabled, missing permission) must not hide the
18907
+ * others — their POIs are then resolved one by one by fetchTagElementsById().
18908
+ */
18909
+ async getTagElementsForSpace(spaceId) {
18910
+ const elements = new Map();
18911
+ const [tickets, equipments, objects3D, features, comments] = await Promise.allSettled([
18912
+ this.ticketService.getTicketsBySpace(spaceId),
18913
+ this.equipmentService.getEquipmentsForSpace(spaceId),
18914
+ this.object3DService.getObjects3DBySpace(spaceId),
18915
+ this.featureService.getAllFeaturesBySpaceRaw(spaceId),
18916
+ // a measurement POI is bound to the measurement's Comment, not to the measurement
18917
+ this.commentService.getCommentsBySpaceID(spaceId)
18918
+ ]);
18919
+ const index = (settled, type) => {
18920
+ if (settled.status === 'fulfilled') {
18921
+ settled.value?.forEach(item => {
18922
+ if (item?.id) {
18923
+ elements.set(item.id, this.toTagElement(type, { spaceID: spaceId, ...item }));
18924
+ }
18925
+ });
18926
+ }
18927
+ };
18928
+ index(tickets, 'ticket');
18929
+ index(equipments, 'equipment');
18930
+ index(objects3D, 'object3D');
18931
+ index(features, 'feature');
18932
+ index(comments, 'comment');
18933
+ return elements;
18934
+ }
18935
+ /**
18936
+ * Fetches by ID the elements the space-wide lists did not return. The IDs are packed into
18937
+ * a few GraphQL requests using aliases (`e0: getTicket(id: ...) e1: getEquipment(...)`),
18938
+ * instead of one request per element: a space can hold over a thousand orphan POIs.
18939
+ * An element that no longer exists comes back as null and is simply not indexed.
18940
+ */
18941
+ async fetchTagElementsById(pois, elements) {
18942
+ const IDS_PER_REQUEST = 100;
18943
+ const queries = {
18944
+ ticket: 'getTicket(id: $ID) { id spaceID title description }',
18945
+ equipment: 'getEquipment(id: $ID) { id spaceID name serialNumber }',
18946
+ object3D: 'getObject3D(id: $ID) { id spaceID name description }',
18947
+ comment: 'getComment(id: $ID) { id spaceID title description annexe }',
18948
+ feature: 'getFeature(id: $ID) { id spaceID title description }'
18949
+ };
18950
+ const pending = new Map();
18951
+ for (const poi of pois) {
18952
+ pending.set(poi.elementID, this.tagElementTypeOf(poi.type));
18953
+ }
18954
+ const entries = [...pending.entries()];
18955
+ const batches = [];
18956
+ for (let i = 0; i < entries.length; i += IDS_PER_REQUEST) {
18957
+ batches.push(entries.slice(i, i + IDS_PER_REQUEST));
18958
+ }
18959
+ await Promise.all(batches.map(async (batch) => {
18960
+ const fields = batch.map(([id, type], i) => `e${i}: ${queries[type].replace('$ID', JSON.stringify(id))}`);
18961
+ let data;
18962
+ try {
18963
+ data = (await API.graphql(graphqlOperation(`query TagElements { ${fields.join('\n')} }`))).data;
18964
+ }
18965
+ catch (error) {
18966
+ // Amplify rejects when ANY alias errors (e.g. an unreadable record), but still
18967
+ // hands over the data of the aliases that succeeded.
18968
+ data = error?.data;
18969
+ if (!data) {
18970
+ console.error('Failed to fetch tag elements by ID:', error);
18971
+ return;
18972
+ }
18973
+ }
18974
+ batch.forEach(([id, type], i) => {
18975
+ const item = data[`e${i}`];
18976
+ if (item) {
18977
+ elements.set(id, this.toTagElement(type, item));
18978
+ }
18979
+ });
18980
+ }));
18981
+ }
18982
+ /**
18983
+ * Moves a stored coordinate. Only the position and the horizontal orientation change:
18984
+ * every other key is kept as is. A coordinate often carries the tag's orientation next to
18985
+ * its position ({x, y, z, xRotation, yRotation}); rebuilding it from the position alone
18986
+ * silently erased that orientation.
18987
+ */
18988
+ getRecalibratedCoordinate(coordinate, transform) {
18989
+ const position = applyRigidTransformToPoint({ x: coordinate.x, y: coordinate.y, z: coordinate.z }, transform);
18990
+ const moved = { ...coordinate, x: position.x, y: position.y, z: position.z };
18991
+ if ('yRotation' in coordinate) {
18992
+ moved.yRotation = applyRigidTransformToYRotationDeg(coordinate.yRotation, transform);
18993
+ }
18994
+ return moved;
18995
+ }
18996
+ /**
18997
+ * Reads POIs by ID, 100 per GraphQL request (aliases). Missing POIs are skipped. Amplify
18998
+ * rejects a request when ANY alias errors, but still hands over the data of the others:
18999
+ * those are kept, so one unreadable POI does not hide the 99 others of its batch.
19000
+ */
19001
+ async fetchPoisById(ids) {
19002
+ const IDS_PER_REQUEST = 100;
19003
+ const uniqueIds = [...new Set(ids)];
19004
+ const batches = [];
19005
+ for (let i = 0; i < uniqueIds.length; i += IDS_PER_REQUEST) {
19006
+ batches.push(uniqueIds.slice(i, i + IDS_PER_REQUEST));
19007
+ }
19008
+ const results = await Promise.all(batches.map(async (batch) => {
19009
+ const fields = batch.map((id, i) => `p${i}: getPOI(id: ${JSON.stringify(id)}) { id coordinate metadata }`);
19010
+ let data;
19011
+ try {
19012
+ data = (await API.graphql(graphqlOperation(`query SelectedPois { ${fields.join('\n')} }`))).data;
19013
+ }
19014
+ catch (error) {
19015
+ data = error?.data;
19016
+ if (!data) {
19017
+ console.error('Failed to read the selected POIs:', error);
19018
+ return [];
19019
+ }
19020
+ }
19021
+ return batch.map((_, i) => data[`p${i}`]).filter(poi => !!poi);
19022
+ }));
19023
+ return results.flat();
19024
+ }
19025
+ tagElementTypeOf(poiType) {
19026
+ switch (poiType) {
19027
+ case PoiType.TICKET: return 'ticket';
19028
+ case PoiType.EQUIPMENT: return 'equipment';
19029
+ case PoiType.OBJECT3D: return 'object3D';
19030
+ case PoiType.MEASURE: return 'comment';
19031
+ default: return 'feature';
19032
+ }
19033
+ }
19034
+ /** Display name + identity fields of an element, per the copy's duplicate-detection rules. */
19035
+ toTagElement(type, item) {
19036
+ const spaceId = item.spaceID;
19037
+ const text = (value) => this.stripHtml(value) || undefined;
19038
+ switch (type) {
19039
+ case 'equipment':
19040
+ return { name: item.name || '', identity: [item.name, item.serialNumber], spaceId, description: text(item.description) };
19041
+ case 'object3D':
19042
+ return { name: item.name || '', identity: [item.name], spaceId, description: text(item.description) };
19043
+ case 'comment':
19044
+ // A measurement's title is usually empty: its description holds the dimensions and
19045
+ // becomes the name. Its picture is the screenshot taken when it was measured.
19046
+ return {
19047
+ name: item.title || item.description || '',
19048
+ identity: [item.title, item.description],
19049
+ spaceId,
19050
+ description: item.title ? text(item.description) : undefined,
19051
+ imageKey: item.annexe || undefined
19052
+ };
19053
+ default:
19054
+ return { name: item.title || '', identity: [item.title, item.description], spaceId, description: text(item.description) };
19055
+ }
19056
+ }
19057
+ stripHtml(value) {
19058
+ return (value || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
19059
+ }
19060
+ /**
19061
+ * Recalibrates a specific list of existing tags in the target space by applying the rigid
19062
+ * transformation calculated from the selected landmark points.
19063
+ *
19064
+ * Each tag is updated on its own: a failing update is logged and counted, it no longer
19065
+ * stops the remaining tags. Progress and log go through copyProgress$ / copyLog$, like a
19066
+ * copy (phase 'recalibrating'). `tagLabels` (tag id -> name) only makes the log readable.
19067
+ */
19068
+ async recalibrateTagsFromVisit(targetSpaceId, selectedTagIds, landmarkSourceA, landmarkTargetA, landmarkSourceB, landmarkTargetB, tagLabels = {}) {
19069
+ const result = { updated: 0, skipped: 0, failed: 0 };
19070
+ const uniqueIds = [...new Set(selectedTagIds)];
19071
+ const total = uniqueIds.length;
19072
+ const label = (id) => tagLabels[id] || `#${id.substring(0, 8)}`;
19073
+ let processed = 0;
19074
+ const emitProgress = (phase) => {
19075
+ this.copyProgress$.next({
19076
+ phase,
19077
+ processedInPhase: processed, totalInPhase: total,
19078
+ processedOverall: processed, totalOverall: total,
19079
+ percent: total > 0 ? Math.round((processed / total) * 100) : 100,
19080
+ errorCount: result.failed, healedCount: 0
19081
+ });
19082
+ this.setStepLog('recalibrating', this.translate.instant('matterportCopy.recalibrateStep', { processed, total }));
19083
+ };
19084
+ this.resetLog();
19085
+ emitProgress('recalibrating');
19086
+ try {
19087
+ const transform = computeRigidTransform(landmarkSourceA, landmarkTargetA, landmarkSourceB, landmarkTargetB);
19088
+ // Re-read only the selected POIs, by ID, so the transform is applied to their current
19089
+ // position — scanning the whole space again for a handful of tags was the slow path.
19090
+ const targetPois = await this.fetchPoisById(uniqueIds);
19091
+ const foundIds = new Set(targetPois.map(poi => poi.id));
19092
+ uniqueIds.filter(id => !foundIds.has(id)).forEach(id => {
19093
+ result.skipped++;
19094
+ processed++;
19095
+ this.appendErrorLog(this.translate.instant('matterportCopy.recalibrateNotFound', { name: label(id) }));
19096
+ });
19097
+ emitProgress('recalibrating');
19098
+ const UPDATES_IN_PARALLEL = 10;
19099
+ for (let i = 0; i < targetPois.length; i += UPDATES_IN_PARALLEL) {
19100
+ await Promise.all(targetPois.slice(i, i + UPDATES_IN_PARALLEL).map(async (poi) => {
19101
+ const id = poi.id;
19102
+ try {
19103
+ let originalCoord = null;
19104
+ try {
19105
+ originalCoord = typeof poi.coordinate === 'string'
19106
+ ? JSON.parse(poi.coordinate)
19107
+ : poi.coordinate;
19108
+ }
19109
+ catch {
19110
+ originalCoord = null;
19111
+ }
19112
+ if (!originalCoord || typeof originalCoord.x !== 'number') {
19113
+ // virtual POI or unreadable coordinate: no position to move
19114
+ result.skipped++;
19115
+ this.appendErrorLog(this.translate.instant('matterportCopy.recalibrateSkipped', { name: label(id) }));
19116
+ return;
19117
+ }
19118
+ await this.poiService.updatePoi({
19119
+ id,
19120
+ coordinate: JSON.stringify(this.getRecalibratedCoordinate(originalCoord, transform)),
19121
+ metadata: this.getAdjustedPoiMetadata(poi.metadata, transform)
19122
+ });
19123
+ result.updated++;
19124
+ }
19125
+ catch (error) {
19126
+ result.failed++;
19127
+ console.error(`Failed to recalibrate the tag ${id}:`, error);
19128
+ this.appendErrorLog(this.translate.instant('matterportCopy.recalibrateError', { name: label(id), error: toErrorMessage(error) }));
19129
+ }
19130
+ finally {
19131
+ processed++;
19132
+ emitProgress('recalibrating');
19133
+ }
19134
+ }));
19135
+ }
19136
+ }
19137
+ catch (error) {
19138
+ // transform or reading failure: nothing left can be moved
19139
+ console.error('Erreur lors du recalibrage des tags:', error);
19140
+ result.failed = total - result.updated - result.skipped;
19141
+ processed = total;
19142
+ this.appendErrorLog(this.translate.instant('matterportCopy.errorGeneral', { error: toErrorMessage(error) }));
19143
+ }
19144
+ emitProgress('done');
19145
+ this.setStepLog('done', this.translate.instant('matterportCopy.recalibrateSummary', result));
19146
+ return result;
19147
+ }
19148
+ 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
19149
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportTagCopyService, providedIn: 'root' });
18550
19150
  }
18551
19151
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MatterportTagCopyService, decorators: [{
18552
19152
  type: Injectable,
18553
19153
  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 }] });
19154
+ }], 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
19155
 
18556
19156
  /* eslint-disable class-methods-use-this */
18557
19157
  class OrganisationService {