diff --git a/typescript/frontend-marios2/package-lock.json b/typescript/frontend-marios2/package-lock.json
index 8658486f9..4eaee6681 100644
--- a/typescript/frontend-marios2/package-lock.json
+++ b/typescript/frontend-marios2/package-lock.json
@@ -42,6 +42,7 @@
"react-icons": "^4.11.0",
"react-icons-converter": "^1.1.4",
"react-intl": "^6.4.4",
+ "react-joyride": "^2.9.3",
"react-redux": "^8.1.3",
"react-router": "6.3.0",
"react-router-dom": "6.3.0",
@@ -2876,6 +2877,11 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@gilbarbara/deep-equal": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz",
+ "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw=="
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
@@ -8195,6 +8201,12 @@
"integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
"dev": true
},
+ "node_modules/deep-diff": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz",
+ "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -8205,7 +8217,6 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -11465,6 +11476,11 @@
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
"integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g=="
},
+ "node_modules/is-lite": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz",
+ "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw=="
+ },
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
@@ -15414,6 +15430,16 @@
"node": ">=4"
}
},
+ "node_modules/popper.js": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
+ "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/possible-typed-array-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
@@ -17273,6 +17299,41 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
+ "node_modules/react-floater": {
+ "version": "0.7.9",
+ "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz",
+ "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==",
+ "dependencies": {
+ "deepmerge": "^4.3.1",
+ "is-lite": "^0.8.2",
+ "popper.js": "^1.16.0",
+ "prop-types": "^15.8.1",
+ "tree-changes": "^0.9.1"
+ },
+ "peerDependencies": {
+ "react": "15 - 18",
+ "react-dom": "15 - 18"
+ }
+ },
+ "node_modules/react-floater/node_modules/@gilbarbara/deep-equal": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz",
+ "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA=="
+ },
+ "node_modules/react-floater/node_modules/is-lite": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz",
+ "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw=="
+ },
+ "node_modules/react-floater/node_modules/tree-changes": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz",
+ "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==",
+ "dependencies": {
+ "@gilbarbara/deep-equal": "^0.1.1",
+ "is-lite": "^0.8.2"
+ }
+ },
"node_modules/react-flow-renderer": {
"version": "10.3.17",
"resolved": "https://registry.npmjs.org/react-flow-renderer/-/react-flow-renderer-10.3.17.tgz",
@@ -17392,6 +17453,15 @@
"react": ">=16.3.0"
}
},
+ "node_modules/react-innertext": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz",
+ "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==",
+ "peerDependencies": {
+ "@types/react": ">=0.0.0 <=99",
+ "react": ">=0.0.0 <=99"
+ }
+ },
"node_modules/react-intl": {
"version": "6.6.8",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.8.tgz",
@@ -17423,6 +17493,44 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
+ "node_modules/react-joyride": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.9.3.tgz",
+ "integrity": "sha512-1+Mg34XK5zaqJ63eeBhqdbk7dlGCFp36FXwsEvgpjqrtyywX2C6h9vr3jgxP0bGHCw8Ilsp/nRDzNVq6HJ3rNw==",
+ "dependencies": {
+ "@gilbarbara/deep-equal": "^0.3.1",
+ "deep-diff": "^1.0.2",
+ "deepmerge": "^4.3.1",
+ "is-lite": "^1.2.1",
+ "react-floater": "^0.7.9",
+ "react-innertext": "^1.1.5",
+ "react-is": "^16.13.1",
+ "scroll": "^3.0.1",
+ "scrollparent": "^2.1.0",
+ "tree-changes": "^0.11.2",
+ "type-fest": "^4.27.0"
+ },
+ "peerDependencies": {
+ "react": "15 - 18",
+ "react-dom": "15 - 18"
+ }
+ },
+ "node_modules/react-joyride/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-joyride/node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/react-redux": {
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz",
@@ -18263,6 +18371,16 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
+ "node_modules/scroll": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz",
+ "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg=="
+ },
+ "node_modules/scrollparent": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz",
+ "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA=="
+ },
"node_modules/select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -19771,6 +19889,15 @@
"node": ">=8"
}
},
+ "node_modules/tree-changes": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.3.tgz",
+ "integrity": "sha512-r14mvDZ6tqz8PRQmlFKjhUVngu4VZ9d92ON3tp0EGpFBE6PAHOq8Bx8m8ahbNoGE3uI/npjYcJiqVydyOiYXag==",
+ "dependencies": {
+ "@gilbarbara/deep-equal": "^0.3.1",
+ "is-lite": "^1.2.1"
+ }
+ },
"node_modules/tryer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
@@ -23174,6 +23301,11 @@
"tslib": "^2.4.0"
}
},
+ "@gilbarbara/deep-equal": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz",
+ "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw=="
+ },
"@humanwhocodes/config-array": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
@@ -27055,6 +27187,11 @@
"integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
"dev": true
},
+ "deep-diff": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz",
+ "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg=="
+ },
"deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -27064,8 +27201,7 @@
"deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "dev": true
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
},
"default-gateway": {
"version": "6.0.3",
@@ -29458,6 +29594,11 @@
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
"integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g=="
},
+ "is-lite": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz",
+ "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw=="
+ },
"is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
@@ -32446,6 +32587,11 @@
}
}
},
+ "popper.js": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+ "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
+ },
"possible-typed-array-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
@@ -33627,6 +33773,39 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
+ "react-floater": {
+ "version": "0.7.9",
+ "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz",
+ "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==",
+ "requires": {
+ "deepmerge": "^4.3.1",
+ "is-lite": "^0.8.2",
+ "popper.js": "^1.16.0",
+ "prop-types": "^15.8.1",
+ "tree-changes": "^0.9.1"
+ },
+ "dependencies": {
+ "@gilbarbara/deep-equal": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz",
+ "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA=="
+ },
+ "is-lite": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz",
+ "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw=="
+ },
+ "tree-changes": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz",
+ "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==",
+ "requires": {
+ "@gilbarbara/deep-equal": "^0.1.1",
+ "is-lite": "^0.8.2"
+ }
+ }
+ }
+ },
"react-flow-renderer": {
"version": "10.3.17",
"resolved": "https://registry.npmjs.org/react-flow-renderer/-/react-flow-renderer-10.3.17.tgz",
@@ -33725,6 +33904,12 @@
"react": ">=16.3.0"
}
},
+ "react-innertext": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz",
+ "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==",
+ "requires": {}
+ },
"react-intl": {
"version": "6.6.8",
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.8.tgz",
@@ -33747,6 +33932,36 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
+ "react-joyride": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.9.3.tgz",
+ "integrity": "sha512-1+Mg34XK5zaqJ63eeBhqdbk7dlGCFp36FXwsEvgpjqrtyywX2C6h9vr3jgxP0bGHCw8Ilsp/nRDzNVq6HJ3rNw==",
+ "requires": {
+ "@gilbarbara/deep-equal": "^0.3.1",
+ "deep-diff": "^1.0.2",
+ "deepmerge": "^4.3.1",
+ "is-lite": "^1.2.1",
+ "react-floater": "^0.7.9",
+ "react-innertext": "^1.1.5",
+ "react-is": "^16.13.1",
+ "scroll": "^3.0.1",
+ "scrollparent": "^2.1.0",
+ "tree-changes": "^0.11.2",
+ "type-fest": "^4.27.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="
+ }
+ }
+ },
"react-redux": {
"version": "8.1.3",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz",
@@ -34334,6 +34549,16 @@
}
}
},
+ "scroll": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz",
+ "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg=="
+ },
+ "scrollparent": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz",
+ "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA=="
+ },
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -35520,6 +35745,15 @@
"punycode": "^2.1.1"
}
},
+ "tree-changes": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.3.tgz",
+ "integrity": "sha512-r14mvDZ6tqz8PRQmlFKjhUVngu4VZ9d92ON3tp0EGpFBE6PAHOq8Bx8m8ahbNoGE3uI/npjYcJiqVydyOiYXag==",
+ "requires": {
+ "@gilbarbara/deep-equal": "^0.3.1",
+ "is-lite": "^1.2.1"
+ }
+ },
"tryer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
diff --git a/typescript/frontend-marios2/package.json b/typescript/frontend-marios2/package.json
index e5a5a5d2f..297b606cd 100644
--- a/typescript/frontend-marios2/package.json
+++ b/typescript/frontend-marios2/package.json
@@ -38,6 +38,7 @@
"react-icons": "^4.11.0",
"react-icons-converter": "^1.1.4",
"react-intl": "^6.4.4",
+ "react-joyride": "^2.9.3",
"react-redux": "^8.1.3",
"react-router": "6.3.0",
"react-router-dom": "6.3.0",
diff --git a/typescript/frontend-marios2/src/App.tsx b/typescript/frontend-marios2/src/App.tsx
index 7ba5ece03..3ff590030 100644
--- a/typescript/frontend-marios2/src/App.tsx
+++ b/typescript/frontend-marios2/src/App.tsx
@@ -22,6 +22,7 @@ import AccessContextProvider from './contexts/AccessContextProvider';
import SalidomoInstallationTabs from './content/dashboards/SalidomoInstallations';
import SodioHomeInstallationTabs from './content/dashboards/SodiohomeInstallations';
import { ProductIdContext } from './contexts/ProductIdContextProvider';
+import { TourProvider } from './contexts/TourContext';
function App() {
const context = useContext(UserContext);
@@ -127,22 +128,28 @@ function App() {
if (!token) {
return (
-
-
- }
- >
- }>
- }
- >
- }
- >
-
+
+
+
+ }
+ >
+ }>
+ }
+ >
+ }
+ >
+
+
);
}
@@ -163,6 +170,7 @@ function App() {
locale={language}
defaultLocale="en"
>
+
@@ -237,6 +245,7 @@ function App() {
+
);
diff --git a/typescript/frontend-marios2/src/config/tourSteps.ts b/typescript/frontend-marios2/src/config/tourSteps.ts
new file mode 100644
index 000000000..f3684278b
--- /dev/null
+++ b/typescript/frontend-marios2/src/config/tourSteps.ts
@@ -0,0 +1,117 @@
+import { Step } from 'react-joyride';
+import { IntlShape } from 'react-intl';
+
+// --- Build a single step with i18n ---
+
+function makeStep(
+ intl: IntlShape,
+ target: string,
+ titleId: string,
+ contentId: string,
+ placement: Step['placement'] = 'bottom',
+ disableBeacon = false
+): Step {
+ return {
+ target,
+ title: intl.formatMessage({ id: titleId }),
+ content: intl.formatMessage({ id: contentId }),
+ placement,
+ ...(disableBeacon ? { disableBeacon: true } : {})
+ };
+}
+
+// --- Tab key → i18n key mapping ---
+
+const tabConfig: Record = {
+ list: { titleId: 'tourListTitle', contentId: 'tourListContent' },
+ tree: { titleId: 'tourTreeTitle', contentId: 'tourTreeContent' },
+ live: { titleId: 'tourLiveTitle', contentId: 'tourLiveContent' },
+ overview: { titleId: 'tourOverviewTitle', contentId: 'tourOverviewContent' },
+ batteryview: { titleId: 'tourBatteryviewTitle', contentId: 'tourBatteryviewContent' },
+ pvview: { titleId: 'tourPvviewTitle', contentId: 'tourPvviewContent' },
+ log: { titleId: 'tourLogTitle', contentId: 'tourLogContent' },
+ information: { titleId: 'tourInformationTitle', contentId: 'tourInformationContent' },
+ report: { titleId: 'tourReportTitle', contentId: 'tourReportContent' },
+ manage: { titleId: 'tourManageTitle', contentId: 'tourManageContent' },
+ configuration: { titleId: 'tourConfigurationTitle', contentId: 'tourConfigurationContent' },
+ history: { titleId: 'tourHistoryTitle', contentId: 'tourHistoryContent' }
+};
+
+// Steps to skip inside a specific installation (already covered in the list-page tour)
+const listPageOnlyTabs = new Set(['list', 'tree']);
+
+// --- Build tour steps from tab value list ---
+
+function buildTourSteps(intl: IntlShape, tabValues: string[], includeInstallationHint = false, isInsideInstallation = false): Step[] {
+ const steps: Step[] = [];
+ if (!isInsideInstallation) {
+ steps.push(makeStep(intl, '[data-tour="language-selector"]', 'tourLanguageTitle', 'tourLanguageContent', 'bottom', true));
+ }
+ for (const value of tabValues) {
+ if (isInsideInstallation && listPageOnlyTabs.has(value)) continue;
+ const cfg = tabConfig[value];
+ if (cfg) {
+ steps.push(makeStep(intl, `#tour-tab-${value}`, cfg.titleId, cfg.contentId, 'bottom', steps.length === 0));
+ }
+ }
+ if (includeInstallationHint && !isInsideInstallation) {
+ steps.push(makeStep(intl, '#tour-tab-list', 'tourExploreTitle', 'tourExploreContent'));
+ }
+ return steps;
+}
+
+// --- Sodistore Home (product 2) ---
+
+export const buildSodiohomeCustomerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'live', 'overview', 'information', 'report'
+], false, inside);
+
+export const buildSodiohomePartnerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'live', 'overview', 'batteryview', 'log', 'information', 'report'
+], true, inside);
+
+export const buildSodiohomeAdminTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'live', 'overview', 'batteryview', 'log', 'manage', 'information', 'configuration', 'history', 'report'
+], true, inside);
+
+// --- Salimax (product 0) / Sodistore Max (product 3) ---
+
+export const buildSalimaxCustomerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'live', 'overview', 'information'
+], false, inside);
+
+export const buildSalimaxPartnerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'live', 'overview', 'batteryview', 'pvview', 'information'
+], true, inside);
+
+export const buildSalimaxAdminTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'live', 'overview', 'batteryview', 'manage', 'log', 'information', 'configuration', 'history', 'pvview'
+], true, inside);
+
+// --- Sodistore Grid (product 4) — same as Salimax but no PV View ---
+
+export const buildSodistoregridCustomerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'live', 'overview', 'information'
+], false, inside);
+
+export const buildSodistoregridPartnerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'live', 'overview', 'batteryview', 'information'
+], true, inside);
+
+export const buildSodistoregridAdminTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'live', 'overview', 'batteryview', 'manage', 'log', 'information', 'configuration', 'history'
+], true, inside);
+
+// --- Salidomo (product 1) ---
+
+export const buildSalidomoCustomerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'batteryview', 'overview', 'information'
+], false, inside);
+
+export const buildSalidomoPartnerTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'batteryview', 'overview', 'information'
+], true, inside);
+
+export const buildSalidomoAdminTourSteps = (intl: IntlShape, inside = false) => buildTourSteps(intl, [
+ 'list', 'tree', 'batteryview', 'overview', 'log', 'manage', 'information', 'history'
+], true, inside);
diff --git a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
index 7ffc7d20f..81121533a 100644
--- a/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/Installations/index.tsx
@@ -412,6 +412,7 @@ function InstallationTabs(props: InstallationTabsProps) {
? routes[tab.value]
: navigateToTabPath(location.pathname, routes[tab.value])
}
+ id={`tour-tab-${tab.value}`}
/>
))}
@@ -480,6 +481,7 @@ function InstallationTabs(props: InstallationTabsProps) {
component={Link}
label={tab.label}
to={routes[tab.value]}
+ id={`tour-tab-${tab.value}`}
/>
))}
diff --git a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx
index f4bd4b5f2..e84691bac 100644
--- a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/index.tsx
@@ -288,6 +288,7 @@ function SalidomoInstallationTabs(props: InstallationTabsProps) {
? routes[tab.value]
: navigateToTabPath(location.pathname, routes[tab.value])
}
+ id={`tour-tab-${tab.value}`}
/>
))}
@@ -349,6 +350,7 @@ function SalidomoInstallationTabs(props: InstallationTabsProps) {
component={Link}
label={tab.label}
to={routes[tab.value]}
+ id={`tour-tab-${tab.value}`}
/>
))}
diff --git a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx
index 5b2e9f0ba..f772ca628 100644
--- a/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx
+++ b/typescript/frontend-marios2/src/content/dashboards/SodiohomeInstallations/index.tsx
@@ -416,6 +416,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
icon={tab.icon}
component={Link}
label={tab.label}
+ id={`tour-tab-${tab.value}`}
to={
tab.value === 'list' || tab.value === 'tree'
? routes[tab.value]
@@ -482,6 +483,7 @@ function SodioHomeInstallationTabs(props: SodioHomeInstallationTabsProps) {
component={Link}
label={tab.label}
to={routes[tab.value]}
+ id={`tour-tab-${tab.value}`}
/>
))}
diff --git a/typescript/frontend-marios2/src/contexts/TourContext.tsx b/typescript/frontend-marios2/src/contexts/TourContext.tsx
new file mode 100644
index 000000000..e43783589
--- /dev/null
+++ b/typescript/frontend-marios2/src/contexts/TourContext.tsx
@@ -0,0 +1,32 @@
+import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
+
+interface TourContextType {
+ runTour: boolean;
+ startTour: () => void;
+ stopTour: () => void;
+}
+
+const TourContext = createContext({
+ runTour: false,
+ startTour: () => {},
+ stopTour: () => {}
+});
+
+export const useTour = () => useContext(TourContext);
+
+interface TourProviderProps {
+ children: ReactNode;
+}
+
+export function TourProvider({ children }: TourProviderProps) {
+ const [runTour, setRunTour] = useState(false);
+
+ const startTour = useCallback(() => setRunTour(true), []);
+ const stopTour = useCallback(() => setRunTour(false), []);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/typescript/frontend-marios2/src/lang/de.json b/typescript/frontend-marios2/src/lang/de.json
index a6a64d8f7..83c4c746a 100644
--- a/typescript/frontend-marios2/src/lang/de.json
+++ b/typescript/frontend-marios2/src/lang/de.json
@@ -469,5 +469,33 @@
"powerW": "Leistung (W)",
"enterPowerValue": "Positiven oder negativen Leistungswert eingeben",
"startDateTime": "Startdatum und -zeit (Startzeit < Stoppzeit)",
- "stopDateTime": "Stoppdatum und -zeit (Startzeit < Stoppzeit)"
+ "stopDateTime": "Stoppdatum und -zeit (Startzeit < Stoppzeit)",
+ "tourLanguageTitle": "Sprache",
+ "tourLanguageContent": "Wählen Sie Ihre bevorzugte Sprache. Die Oberfläche unterstützt Englisch, Deutsch, Französisch und Italienisch.",
+ "tourExploreTitle": "Installation erkunden",
+ "tourExploreContent": "Klicken Sie auf eine Installation, um sie zu öffnen. Klicken Sie darin erneut auf die Tour-Schaltfläche für eine detaillierte Anleitung aller verfügbaren Tabs.",
+ "tourListTitle": "Installationsliste",
+ "tourListContent": "Suchen und durchsuchen Sie alle Ihre Installationen. Klicken Sie auf eine Installation, um deren Dashboard zu öffnen.",
+ "tourTreeTitle": "Ordneransicht",
+ "tourTreeContent": "Ihre Installationen nach Ordnern organisiert. Erweitern Sie Ordner, um Installationen nach Standort zu finden.",
+ "tourLiveTitle": "Live-Daten",
+ "tourLiveContent": "Echtzeitdaten Ihres Systems — Batteriestatus, Energiefluss und Systemstatus, kontinuierlich aktualisiert.",
+ "tourOverviewTitle": "Überblick",
+ "tourOverviewContent": "Visuelle Zusammenfassung mit Diagrammen — Produktion, Verbrauch und Batterieladung im Zeitverlauf. Verwenden Sie die Datumssteuerung, um einen bestimmten Tag oder Zeitraum anzuzeigen.",
+ "tourBatteryviewTitle": "Batterieansicht",
+ "tourBatteryviewContent": "Detaillierte Batterieüberwachung — Ladezustand (%), Energiefluss (kW), Spannung und Strom pro Batterieeinheit.",
+ "tourPvviewTitle": "PV-Ansicht",
+ "tourPvviewContent": "Solaranlagen-Überwachung — Produktionsdaten Ihrer Photovoltaikanlage.",
+ "tourLogTitle": "Protokoll",
+ "tourLogContent": "Geräte-Ereignisprotokolle — Systemereignisse, Warnungen und Fehler im Zeitverlauf.",
+ "tourInformationTitle": "Systeminformationen",
+ "tourInformationContent": "Installationsdetails — Standort, Geräteseriennummern und Firmware-Versionen. Nutzen Sie dies als Referenz bei Kontakt mit dem Support.",
+ "tourReportTitle": "Energieberichte",
+ "tourReportContent": "Energiedaten in kWh anzeigen. Wechseln Sie zwischen wöchentlichen (Montag–Sonntag), monatlichen und jährlichen Berichten, um zu sehen, wie viel Energie produziert, verbraucht oder gespeichert wurde.",
+ "tourManageTitle": "Zugriffsverwaltung",
+ "tourManageContent": "Verwalten Sie, welche Benutzer Zugriff auf diese Installation haben, und legen Sie deren Berechtigungen fest.",
+ "tourConfigurationTitle": "Konfiguration",
+ "tourConfigurationContent": "Geräteeinstellungen für diese Installation anzeigen und ändern.",
+ "tourHistoryTitle": "Verlauf",
+ "tourHistoryContent": "Protokoll der Aktionen an dieser Installation — wer hat was und wann geändert."
}
\ No newline at end of file
diff --git a/typescript/frontend-marios2/src/lang/en.json b/typescript/frontend-marios2/src/lang/en.json
index 6067ffbe6..c9c909b62 100644
--- a/typescript/frontend-marios2/src/lang/en.json
+++ b/typescript/frontend-marios2/src/lang/en.json
@@ -217,5 +217,33 @@
"powerW": "Power (W)",
"enterPowerValue": "Enter a positive or negative power value",
"startDateTime": "Start Date and Time (Start Time < Stop Time)",
- "stopDateTime": "Stop Date and Time (Start Time < Stop Time)"
+ "stopDateTime": "Stop Date and Time (Start Time < Stop Time)",
+ "tourLanguageTitle": "Language",
+ "tourLanguageContent": "Choose your preferred language. The interface supports English, German, French, and Italian.",
+ "tourExploreTitle": "Explore an Installation",
+ "tourExploreContent": "Click any installation to open it. Once inside, click the tour button again for a detailed guide of all available tabs.",
+ "tourListTitle": "Installation List",
+ "tourListContent": "Search and browse all your installations. Click any installation to open its dashboard.",
+ "tourTreeTitle": "Folder View",
+ "tourTreeContent": "Your installations organised in folders. Expand folders to find installations by site or location.",
+ "tourLiveTitle": "Live Data",
+ "tourLiveContent": "Real-time data from your system — battery state, power flow, and system status, updated continuously.",
+ "tourOverviewTitle": "Overview",
+ "tourOverviewContent": "Visual summary with charts — production, consumption, and battery charge over time. Use the date controls to view a specific day or custom range.",
+ "tourBatteryviewTitle": "Battery View",
+ "tourBatteryviewContent": "Detailed battery monitoring — state of charge (%), power flow (kW), voltage, and current per battery unit.",
+ "tourPvviewTitle": "PV View",
+ "tourPvviewContent": "Solar panel monitoring — see production data from your photovoltaic system.",
+ "tourLogTitle": "Log",
+ "tourLogContent": "Device event logs — view system events, warnings, and errors over time.",
+ "tourInformationTitle": "System Information",
+ "tourInformationContent": "Installation details — location, device serial numbers, and firmware versions. Use this as reference if you contact support.",
+ "tourReportTitle": "Energy Reports",
+ "tourReportContent": "View energy data in kWh. Switch between weekly (Monday–Sunday), monthly, and yearly reports to see how much energy was produced, consumed, or stored.",
+ "tourManageTitle": "Access Management",
+ "tourManageContent": "Manage which users have access to this installation and set their permissions.",
+ "tourConfigurationTitle": "Configuration",
+ "tourConfigurationContent": "View and modify device settings for this installation.",
+ "tourHistoryTitle": "History",
+ "tourHistoryContent": "Audit trail of actions performed on this installation — who changed what and when."
}
diff --git a/typescript/frontend-marios2/src/lang/fr.json b/typescript/frontend-marios2/src/lang/fr.json
index c7ceb83e5..1fcba5814 100644
--- a/typescript/frontend-marios2/src/lang/fr.json
+++ b/typescript/frontend-marios2/src/lang/fr.json
@@ -469,5 +469,33 @@
"powerW": "Puissance (W)",
"enterPowerValue": "Entrez une valeur de puissance positive ou négative",
"startDateTime": "Date et heure de début (Début < Fin)",
- "stopDateTime": "Date et heure de fin (Début < Fin)"
+ "stopDateTime": "Date et heure de fin (Début < Fin)",
+ "tourLanguageTitle": "Langue",
+ "tourLanguageContent": "Choisissez votre langue préférée. L'interface est disponible en anglais, allemand, français et italien.",
+ "tourExploreTitle": "Explorer une installation",
+ "tourExploreContent": "Cliquez sur une installation pour l'ouvrir. Une fois à l'intérieur, cliquez à nouveau sur le bouton de visite pour un guide détaillé de tous les onglets disponibles.",
+ "tourListTitle": "Liste des installations",
+ "tourListContent": "Recherchez et parcourez toutes vos installations. Cliquez sur une installation pour ouvrir son tableau de bord.",
+ "tourTreeTitle": "Vue par dossiers",
+ "tourTreeContent": "Vos installations organisées en dossiers. Développez les dossiers pour trouver les installations par site ou emplacement.",
+ "tourLiveTitle": "Données en direct",
+ "tourLiveContent": "Données en temps réel de votre système — état de la batterie, flux d'énergie et état du système, mis à jour en continu.",
+ "tourOverviewTitle": "Aperçu",
+ "tourOverviewContent": "Résumé visuel avec graphiques — production, consommation et charge de la batterie au fil du temps. Utilisez les contrôles de date pour afficher un jour spécifique ou une plage personnalisée.",
+ "tourBatteryviewTitle": "Vue batterie",
+ "tourBatteryviewContent": "Surveillance détaillée de la batterie — état de charge (%), flux d'énergie (kW), tension et courant par unité de batterie.",
+ "tourPvviewTitle": "Vue PV",
+ "tourPvviewContent": "Surveillance des panneaux solaires — consultez les données de production de votre système photovoltaïque.",
+ "tourLogTitle": "Journal",
+ "tourLogContent": "Journaux d'événements — événements système, avertissements et erreurs au fil du temps.",
+ "tourInformationTitle": "Informations système",
+ "tourInformationContent": "Détails de l'installation — emplacement, numéros de série des appareils et versions du firmware. Utilisez ceci comme référence si vous contactez le support.",
+ "tourReportTitle": "Rapports énergétiques",
+ "tourReportContent": "Afficher les données énergétiques en kWh. Basculez entre les rapports hebdomadaires (lundi–dimanche), mensuels et annuels pour voir combien d'énergie a été produite, consommée ou stockée.",
+ "tourManageTitle": "Gestion des accès",
+ "tourManageContent": "Gérez quels utilisateurs ont accès à cette installation et définissez leurs autorisations.",
+ "tourConfigurationTitle": "Configuration",
+ "tourConfigurationContent": "Afficher et modifier les paramètres de l'appareil pour cette installation.",
+ "tourHistoryTitle": "Historique",
+ "tourHistoryContent": "Journal des actions effectuées sur cette installation — qui a changé quoi et quand."
}
\ No newline at end of file
diff --git a/typescript/frontend-marios2/src/lang/it.json b/typescript/frontend-marios2/src/lang/it.json
index 46a0ee780..0ed40875f 100644
--- a/typescript/frontend-marios2/src/lang/it.json
+++ b/typescript/frontend-marios2/src/lang/it.json
@@ -469,5 +469,33 @@
"powerW": "Potenza (W)",
"enterPowerValue": "Inserire un valore di potenza positivo o negativo",
"startDateTime": "Data e ora di inizio (Inizio < Fine)",
- "stopDateTime": "Data e ora di fine (Inizio < Fine)"
+ "stopDateTime": "Data e ora di fine (Inizio < Fine)",
+ "tourLanguageTitle": "Lingua",
+ "tourLanguageContent": "Scegli la tua lingua preferita. L'interfaccia supporta inglese, tedesco, francese e italiano.",
+ "tourExploreTitle": "Esplora un'installazione",
+ "tourExploreContent": "Clicca su un'installazione per aprirla. Una volta dentro, clicca nuovamente sul pulsante del tour per una guida dettagliata di tutte le schede disponibili.",
+ "tourListTitle": "Elenco installazioni",
+ "tourListContent": "Cerca e sfoglia tutte le tue installazioni. Clicca su un'installazione per aprire la sua dashboard.",
+ "tourTreeTitle": "Vista cartelle",
+ "tourTreeContent": "Le tue installazioni organizzate in cartelle. Espandi le cartelle per trovare le installazioni per sito o posizione.",
+ "tourLiveTitle": "Dati in tempo reale",
+ "tourLiveContent": "Dati in tempo reale dal tuo sistema — stato della batteria, flusso di energia e stato del sistema, aggiornati continuamente.",
+ "tourOverviewTitle": "Panoramica",
+ "tourOverviewContent": "Riepilogo visivo con grafici — produzione, consumo e carica della batteria nel tempo. Usa i controlli della data per visualizzare un giorno specifico o un intervallo personalizzato.",
+ "tourBatteryviewTitle": "Vista batteria",
+ "tourBatteryviewContent": "Monitoraggio dettagliato della batteria — stato di carica (%), flusso di energia (kW), tensione e corrente per unità di batteria.",
+ "tourPvviewTitle": "Vista PV",
+ "tourPvviewContent": "Monitoraggio dei pannelli solari — visualizza i dati di produzione del tuo impianto fotovoltaico.",
+ "tourLogTitle": "Registro",
+ "tourLogContent": "Registri degli eventi — eventi di sistema, avvisi ed errori nel tempo.",
+ "tourInformationTitle": "Informazioni di sistema",
+ "tourInformationContent": "Dettagli dell'installazione — posizione, numeri di serie dei dispositivi e versioni firmware. Usa come riferimento se contatti l'assistenza.",
+ "tourReportTitle": "Rapporti energetici",
+ "tourReportContent": "Visualizza i dati energetici in kWh. Passa tra rapporti settimanali (lunedì–domenica), mensili e annuali per vedere quanta energia è stata prodotta, consumata o immagazzinata.",
+ "tourManageTitle": "Gestione accessi",
+ "tourManageContent": "Gestisci quali utenti hanno accesso a questa installazione e imposta i loro permessi.",
+ "tourConfigurationTitle": "Configurazione",
+ "tourConfigurationContent": "Visualizza e modifica le impostazioni del dispositivo per questa installazione.",
+ "tourHistoryTitle": "Cronologia",
+ "tourHistoryContent": "Registro delle azioni eseguite su questa installazione — chi ha cambiato cosa e quando."
}
diff --git a/typescript/frontend-marios2/src/layouts/SidebarLayout/Header/Menu/index.tsx b/typescript/frontend-marios2/src/layouts/SidebarLayout/Header/Menu/index.tsx
index 32ceaf848..7d85b6c90 100644
--- a/typescript/frontend-marios2/src/layouts/SidebarLayout/Header/Menu/index.tsx
+++ b/typescript/frontend-marios2/src/layouts/SidebarLayout/Header/Menu/index.tsx
@@ -112,6 +112,7 @@ function HeaderMenu(props: HeaderButtonsProps) {
sx={{
color: isMobile ? 'white' : ''
}}
+ data-tour="language-selector"
>
`
@@ -44,6 +46,7 @@ interface HeaderProps {
function Header(props: HeaderProps) {
const { sidebarToggle, toggleSidebar } = useContext(SidebarContext);
+ const { startTour } = useTour();
const theme = useTheme();
const isMobile = window.innerWidth <= 1280;
@@ -96,6 +99,14 @@ function Header(props: HeaderProps) {
>
+
+
+
+
+
void;
}
+function getTourSteps(pathname: string, userType: UserType, intl: IntlShape, isInsideInstallation: boolean): Step[] {
+ const role = userType === UserType.admin ? 'admin'
+ : userType === UserType.partner ? 'partner'
+ : 'customer';
+
+ if (pathname.includes('/sodiohome_installations')) {
+ if (role === 'admin') return buildSodiohomeAdminTourSteps(intl, isInsideInstallation);
+ if (role === 'partner') return buildSodiohomePartnerTourSteps(intl, isInsideInstallation);
+ return buildSodiohomeCustomerTourSteps(intl, isInsideInstallation);
+ }
+ if (pathname.includes('/salidomo_installations')) {
+ if (role === 'admin') return buildSalidomoAdminTourSteps(intl, isInsideInstallation);
+ if (role === 'partner') return buildSalidomoPartnerTourSteps(intl, isInsideInstallation);
+ return buildSalidomoCustomerTourSteps(intl, isInsideInstallation);
+ }
+ if (pathname.includes('/sodistoregrid_installations')) {
+ if (role === 'admin') return buildSodistoregridAdminTourSteps(intl, isInsideInstallation);
+ if (role === 'partner') return buildSodistoregridPartnerTourSteps(intl, isInsideInstallation);
+ return buildSodistoregridCustomerTourSteps(intl, isInsideInstallation);
+ }
+ // Salimax (/installations/) and Sodistore Max (/sodistore_installations/)
+ if (role === 'admin') return buildSalimaxAdminTourSteps(intl, isInsideInstallation);
+ if (role === 'partner') return buildSalimaxPartnerTourSteps(intl, isInsideInstallation);
+ return buildSalimaxCustomerTourSteps(intl, isInsideInstallation);
+}
+
const SidebarLayout = (props: SidebarLayoutProps) => {
const theme = useTheme();
+ const intl = useIntl();
+ const { runTour, stopTour } = useTour();
+ const location = useLocation();
+ const { currentUser } = useContext(UserContext);
+ const [tourSteps, setTourSteps] = useState([]);
+ const [tourReady, setTourReady] = useState(false);
+
+ useEffect(() => {
+ if (!runTour) {
+ setTourReady(false);
+ return;
+ }
+ // Delay to let child components render their tour target elements
+ const timer = setTimeout(() => {
+ const userType = currentUser?.userType ?? UserType.client;
+ const isInsideInstallation = location.pathname.includes('/installation/');
+ const steps = getTourSteps(location.pathname, userType, intl, isInsideInstallation);
+ const filtered = steps.filter((step) => {
+ if (typeof step.target === 'string') {
+ return document.querySelector(step.target) !== null;
+ }
+ return true;
+ });
+ setTourSteps(filtered);
+ setTourReady(true);
+ }, 300);
+ return () => clearTimeout(timer);
+ }, [runTour, location.pathname, currentUser?.userType, intl]);
+
+ const handleJoyrideCallback = (data: CallBackProps) => {
+ const { status } = data;
+ if (status === STATUS.FINISHED || status === STATUS.SKIPPED) {
+ stopTour();
+ }
+ };
return (
<>
+