diff --git a/package-lock.json b/package-lock.json index 6533cf0..a61b400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "smatec-frontend", + "name": "SMATEC-FRONTEND", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10eacda..6155c17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,15 @@ importers: dayjs: specifier: ^1.11.19 version: 1.11.19 + moment: + specifier: ^2.30.1 + version: 2.30.1 + ol: + specifier: ^10.6.1 + version: 10.7.0 + reconnecting-websocket: + specifier: ^4.4.0 + version: 4.4.0 devDependencies: '@types/react': specifier: ^18.0.33 @@ -1091,6 +1100,9 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, tarball: https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz} engines: {node: '>= 8'} + '@petamoriken/float16@3.9.3': + resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==, tarball: https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.3.tgz} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, tarball: https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz} engines: {node: '>=14'} @@ -1356,6 +1368,9 @@ packages: '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==, tarball: https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz} + '@types/rbush@4.0.0': + resolution: {integrity: sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==, tarball: https://registry.npmjs.org/@types/rbush/-/rbush-4.0.0.tgz} + '@types/react-dom@18.3.7': resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==, tarball: https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz} peerDependencies: @@ -2746,6 +2761,9 @@ packages: react: 15.x || ^16.0.0-0 react-dom: 15.x || ^16.0.0-0 + earcut@3.0.2: + resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==, tarball: https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, tarball: https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz} @@ -3179,6 +3197,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, tarball: https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz} engines: {node: '>=6.9.0'} + geotiff@2.1.3: + resolution: {integrity: sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==, tarball: https://registry.npmjs.org/geotiff/-/geotiff-2.1.3.tgz} + engines: {node: '>=10.19'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, tarball: https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz} engines: {node: 6.* || 8.* || >= 10.*} @@ -3840,6 +3862,9 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==, tarball: https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz} + lerc@3.0.0: + resolution: {integrity: sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==, tarball: https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz} + less-loader@12.3.0: resolution: {integrity: sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==, tarball: https://registry.npmjs.org/less-loader/-/less-loader-12.3.0.tgz} engines: {node: '>= 18.12.0'} @@ -4281,6 +4306,9 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==, tarball: https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz} + ol@10.7.0: + resolution: {integrity: sha512-122U5gamPqNgLpLOkogFJhgpywvd/5en2kETIDW+Ubfi9lPnZ0G9HWRdG+CX0oP8od2d6u6ky3eewIYYlrVczw==, tarball: https://registry.npmjs.org/ol/-/ol-10.7.0.tgz} + on-exit-leak-free@0.2.0: resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==, tarball: https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz} @@ -4352,6 +4380,9 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==, tarball: https://registry.npmjs.org/pako/-/pako-1.0.11.tgz} + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==, tarball: https://registry.npmjs.org/pako/-/pako-2.1.0.tgz} + param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==, tarball: https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz} @@ -4363,6 +4394,9 @@ packages: resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==, tarball: https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz} engines: {node: '>= 0.10'} + parse-headers@2.0.6: + resolution: {integrity: sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==, tarball: https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz} + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, tarball: https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz} engines: {node: '>=8'} @@ -4421,6 +4455,10 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==, tarball: https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz} engines: {node: '>=8'} + pbf@4.0.1: + resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==, tarball: https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz} + hasBin: true + pbkdf2@3.1.5: resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==, tarball: https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz} engines: {node: '>= 0.10'} @@ -4835,6 +4873,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, tarball: https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz} + protocol-buffers-schema@3.6.0: + resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==, tarball: https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==, tarball: https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz} engines: {node: '>= 0.10'} @@ -4883,6 +4924,13 @@ packages: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==, tarball: https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz} engines: {node: '>=8'} + quick-lru@6.1.2: + resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==, tarball: https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz} + engines: {node: '>=12'} + + quickselect@3.0.0: + resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==, tarball: https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz} + randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==, tarball: https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz} @@ -4897,6 +4945,9 @@ packages: resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==, tarball: https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz} engines: {node: '>= 0.8'} + rbush@4.0.1: + resolution: {integrity: sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==, tarball: https://registry.npmjs.org/rbush/-/rbush-4.0.1.tgz} + rc-align@4.0.15: resolution: {integrity: sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA==, tarball: https://registry.npmjs.org/rc-align/-/rc-align-4.0.15.tgz} peerDependencies: @@ -5462,6 +5513,9 @@ packages: resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==, tarball: https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz} engines: {node: '>= 12.13.0'} + reconnecting-websocket@4.4.0: + resolution: {integrity: sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==, tarball: https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, tarball: https://registry.npmjs.org/redent/-/redent-3.0.0.tgz} engines: {node: '>=8'} @@ -5538,6 +5592,9 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, tarball: https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz} + resolve-protobuf-schema@2.1.0: + resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==, tarball: https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz} + resolve-url-loader@5.0.0: resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==, tarball: https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz} engines: {node: '>=12'} @@ -6377,6 +6434,9 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==, tarball: https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz} engines: {node: '>= 8'} + web-worker@1.5.0: + resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==, tarball: https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz} + webpack-5-chain@8.0.1: resolution: {integrity: sha512-Tu1w80WA2Z+X6e7KzGy+cc0A0z+npVJA/fh55q2azMJ030gqz343Kx+yNAstDCeugsepmtDWY2J2IBRW/O+DEA==, tarball: https://registry.npmjs.org/webpack-5-chain/-/webpack-5-chain-8.0.1.tgz} engines: {node: '>=10'} @@ -6454,6 +6514,9 @@ packages: utf-8-validate: optional: true + xml-utils@1.10.2: + resolution: {integrity: sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==, tarball: https://registry.npmjs.org/xml-utils/-/xml-utils-1.10.2.tgz} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, tarball: https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz} engines: {node: '>=0.4'} @@ -6501,6 +6564,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==, tarball: https://registry.npmjs.org/zod/-/zod-3.25.76.tgz} + zstddec@0.1.0: + resolution: {integrity: sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==, tarball: https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz} + snapshots: '@ahooksjs/use-request@2.8.15(react@18.3.1)': @@ -7810,6 +7876,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@petamoriken/float16@3.9.3': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -8095,6 +8163,8 @@ snapshots: '@types/prop-types@15.7.15': {} + '@types/rbush@4.0.0': {} + '@types/react-dom@18.3.7(@types/react@18.3.27)': dependencies: '@types/react': 18.3.27 @@ -10061,6 +10131,8 @@ snapshots: react-router-redux: 5.0.0-alpha.9(react@18.3.1) redux: 3.7.2 + earcut@3.0.2: {} + eastasianwidth@0.2.0: {} ee-first@1.1.1: {} @@ -10704,6 +10776,17 @@ snapshots: gensync@1.0.0-beta.2: {} + geotiff@2.1.3: + dependencies: + '@petamoriken/float16': 3.9.3 + lerc: 3.0.0 + pako: 2.1.0 + parse-headers: 2.0.6 + quick-lru: 6.1.2 + web-worker: 1.5.0 + xml-utils: 1.10.2 + zstddec: 0.1.0 + get-caller-file@2.0.5: {} get-intrinsic@1.3.0: @@ -11398,6 +11481,8 @@ snapshots: kolorist@1.8.0: {} + lerc@3.0.0: {} + less-loader@12.3.0(less@4.5.1)(webpack@5.104.1): dependencies: less: 4.5.1 @@ -11877,6 +11962,14 @@ snapshots: obuf@1.1.2: {} + ol@10.7.0: + dependencies: + '@types/rbush': 4.0.0 + earcut: 3.0.2 + geotiff: 2.1.3 + pbf: 4.0.1 + rbush: 4.0.1 + on-exit-leak-free@0.2.0: {} on-finished@2.3.0: @@ -11953,6 +12046,8 @@ snapshots: pako@1.0.11: {} + pako@2.1.0: {} + param-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -11970,6 +12065,8 @@ snapshots: pbkdf2: 3.1.5 safe-buffer: 5.2.1 + parse-headers@2.0.6: {} + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.28.6 @@ -12017,6 +12114,10 @@ snapshots: path-type@4.0.0: {} + pbf@4.0.1: + dependencies: + resolve-protobuf-schema: 2.1.0 + pbkdf2@3.1.5: dependencies: create-hash: 1.2.0 @@ -12396,6 +12497,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + protocol-buffers-schema@3.6.0: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -12447,6 +12550,10 @@ snapshots: quick-lru@4.0.1: {} + quick-lru@6.1.2: {} + + quickselect@3.0.0: {} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -12465,6 +12572,10 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + rbush@4.0.1: + dependencies: + quickselect: 3.0.0 + rc-align@4.0.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.28.6 @@ -13262,6 +13373,8 @@ snapshots: real-require@0.1.0: {} + reconnecting-websocket@4.4.0: {} + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -13340,6 +13453,10 @@ snapshots: resolve-pkg-maps@1.0.0: {} + resolve-protobuf-schema@2.1.0: + dependencies: + protocol-buffers-schema: 3.6.0 + resolve-url-loader@5.0.0: dependencies: adjust-sourcemap-loader: 4.0.0 @@ -14334,6 +14451,8 @@ snapshots: web-streams-polyfill@3.3.3: {} + web-worker@1.5.0: {} + webpack-5-chain@8.0.1: dependencies: deepmerge: 1.5.2 @@ -14447,6 +14566,8 @@ snapshots: ws@8.19.0: {} + xml-utils@1.10.2: {} + xtend@4.0.2: {} y18n@5.0.8: {} @@ -14480,3 +14601,5 @@ snapshots: zod: 3.25.76 zod@3.25.76: {} + + zstddec@0.1.0: {} diff --git a/src/app.tsx b/src/app.tsx index 6ebd4ce..efc70a0 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -86,7 +86,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => { contentWidth: 'Fluid', navTheme: isDark ? 'realDark' : 'light', splitMenus: true, - iconfontUrl: '//at.alicdn.com/t/c/font_5096559_fvnh1x2eqer.js', + iconfontUrl: '//at.alicdn.com/t/c/font_5096559_pw36kpy0e7h.js', contentStyle: { padding: 0, margin: 0, diff --git a/src/locales/en-US/master/master-thing-en.ts b/src/locales/en-US/master/master-thing-en.ts index e0b4a42..d4e4306 100644 --- a/src/locales/en-US/master/master-thing-en.ts +++ b/src/locales/en-US/master/master-thing-en.ts @@ -3,4 +3,33 @@ export default { 'master.thing.external_id': 'External ID', 'master.thing.group': 'Group', 'master.thing.address': 'Address', + + // Device translations + 'master.devices.title': 'Devices', + 'master.devices.name': 'Name', + 'master.devices.name.tip': 'The device name', + 'master.devices.external_id': 'External ID', + 'master.devices.external_id.tip': 'The external identifier', + 'master.devices.type': 'Type', + 'master.devices.type.tip': 'The device type', + 'master.devices.online': 'Online', + 'master.devices.offline': 'Offline', + 'master.devices.table.pagination': 'devices', + 'master.devices.register': 'Add Device', + 'master.devices.register.title': 'Add new device', + 'master.devices.create.success': 'Device created successfully', + 'master.devices.create.error': 'Device creation failed', + 'master.devices.groups': 'Groups', + 'master.devices.groups.required': 'Please select groups', + // Edit device modal + 'master.devices.update.title': 'Update device', + 'master.devices.ok': 'OK', + 'master.devices.cancel': 'Cancel', + 'master.devices.name.placeholder': 'Enter device name', + 'master.devices.name.required': 'Please enter device name', + 'master.devices.external_id.placeholder': 'Enter external ID', + 'master.devices.external_id.required': 'Please enter external ID', + 'master.devices.address': 'Address', + 'master.devices.address.placeholder': 'Enter address', + 'master.devices.address.required': 'Please enter address', }; diff --git a/src/locales/vi-VN/master/master-thing-vi.ts b/src/locales/vi-VN/master/master-thing-vi.ts index 24d3882..8361fc3 100644 --- a/src/locales/vi-VN/master/master-thing-vi.ts +++ b/src/locales/vi-VN/master/master-thing-vi.ts @@ -3,4 +3,32 @@ export default { 'master.thing.external_id': 'External ID', 'master.thing.group': 'Nhóm', 'master.thing.address': 'Địa chỉ', + // Device translations + 'master.devices.title': 'Quản lý thiết bị', + 'master.devices.name': 'Tên', + 'master.devices.name.tip': 'Tên thiết bị', + 'master.devices.external_id': 'External ID', + 'master.devices.external_id.tip': 'Mã định danh bên ngoài', + 'master.devices.type': 'Loại', + 'master.devices.type.tip': 'Loại thiết bị', + 'master.devices.online': 'Trực tuyến', + 'master.devices.offline': 'Ngoại tuyến', + 'master.devices.table.pagination': 'thiết bị', + 'master.devices.register': 'Thêm thiết bị', + 'master.devices.register.title': 'Thêm thiết bị mới', + 'master.devices.create.success': 'Tạo thiết bị thành công', + 'master.devices.create.error': 'Tạo thiết bị lỗi', + 'master.devices.groups': 'Đơn vị', + 'master.devices.groups.required': 'Vui lòng chọn đơn vị', + // Edit device modal + 'master.devices.update.title': 'Cập nhật thiết bị', + 'master.devices.ok': 'Đồng ý', + 'master.devices.cancel': 'Hủy', + 'master.devices.name.placeholder': 'Nhập tên thiết bị', + 'master.devices.name.required': 'Vui lòng nhập tên thiết bị', + 'master.devices.external_id.placeholder': 'Nhập mã định danh bên ngoài', + 'master.devices.external_id.required': 'Vui lòng nhập mã định danh bên ngoài', + 'master.devices.address': 'Địa chỉ', + 'master.devices.address.placeholder': 'Nhập địa chỉ', + 'master.devices.address.required': 'Vui lòng nhập địa chỉ', }; diff --git a/src/pages/Manager/Device/components/CreateDevice.tsx b/src/pages/Manager/Device/components/CreateDevice.tsx new file mode 100644 index 0000000..d89fa67 --- /dev/null +++ b/src/pages/Manager/Device/components/CreateDevice.tsx @@ -0,0 +1,210 @@ +import TreeSelectedGroup from '@/components/shared/TreeSelectedGroup'; +import { PlusOutlined } from '@ant-design/icons'; +import { + ModalForm, + ProForm, + ProFormInstance, + ProFormSelect, + ProFormText, +} from '@ant-design/pro-components'; +import { FormattedMessage, useIntl } from '@umijs/max'; +import { Button } from 'antd'; +import { MessageInstance } from 'antd/es/message/interface'; +import { useRef, useState } from 'react'; + +type CreateDeviceProps = { + message: MessageInstance; + onSuccess?: (isSuccess: boolean) => void; +}; + +type CreateDeviceFormValues = { + name: string; + external_id: string; + type: string; + address?: string; + group_id?: string; +}; + +const CreateDevice = ({ message, onSuccess }: CreateDeviceProps) => { + const formRef = useRef>(); + const intl = useIntl(); + const [group_id, setGroupId] = useState(null); + + const handleGroupSelect = (group: string | string[] | null) => { + setGroupId(group); + formRef.current?.setFieldsValue({ + group_id: Array.isArray(group) ? group.join(',') : group || undefined, + }); + }; + + return ( + + title={intl.formatMessage({ + id: 'master.devices.register.title', + defaultMessage: 'Register New Device', + })} + formRef={formRef} + trigger={ + + } + autoFocusFirstInput + onFinish={async (values: CreateDeviceFormValues) => { + // TODO: Implement API call to create device + console.log('Create device with values:', values); + + try { + // Placeholder for API call + // const body = { + // name: values.name, + // metadata: { + // external_id: values.external_id, + // type: values.type, + // address: values.address, + // group_id: values.group_id, + // }, + // }; + // const resp = await apiCreateDevice(body); + + message.success( + intl.formatMessage({ + id: 'master.devices.create.success', + defaultMessage: 'Create device successfully', + }), + ); + formRef.current?.resetFields(); + onSuccess?.(true); + return true; + } catch (error) { + message.error( + intl.formatMessage({ + id: 'master.devices.create.error', + defaultMessage: 'Failed to create device', + }), + ); + onSuccess?.(false); + return false; + } + }} + > + + + + + + + + + + + + + ); +}; + +export default CreateDevice; diff --git a/src/pages/Manager/Device/components/EditDeviceModal.tsx b/src/pages/Manager/Device/components/EditDeviceModal.tsx new file mode 100644 index 0000000..9db5969 --- /dev/null +++ b/src/pages/Manager/Device/components/EditDeviceModal.tsx @@ -0,0 +1,133 @@ +import { useIntl } from '@umijs/max'; +import { Form, Input, Modal } from 'antd'; +import React, { useEffect } from 'react'; + +interface Props { + visible: boolean; + device: MasterModel.Thing | null; + onCancel: () => void; + onSubmit: (values: { + name: string; + external_id: string; + address?: string; + }) => void; +} + +const EditDeviceModal: React.FC = ({ + visible, + device, + onCancel, + onSubmit, +}) => { + const [form] = Form.useForm(); + const intl = useIntl(); + + useEffect(() => { + if (device) { + form.setFieldsValue({ + name: device.name, + external_id: device?.metadata?.external_id, + address: device?.metadata?.address, + }); + } else { + form.resetFields(); + } + }, [device, form]); + + return ( + form.submit()} + okText={intl.formatMessage({ + id: 'master.devices.ok', + defaultMessage: 'OK', + })} + cancelText={intl.formatMessage({ + id: 'master.devices.cancel', + defaultMessage: 'Cancel', + })} + destroyOnClose + > +
+ + + + + + + + + + + +
+
+ ); +}; + +export default EditDeviceModal; diff --git a/src/pages/Manager/Device/index.tsx b/src/pages/Manager/Device/index.tsx index 7e0c900..4ec7c67 100644 --- a/src/pages/Manager/Device/index.tsx +++ b/src/pages/Manager/Device/index.tsx @@ -1,5 +1,319 @@ +import IconFont from '@/components/IconFont'; +import TreeGroup from '@/components/shared/TreeGroup'; +import { DEFAULT_PAGE_SIZE } from '@/constants'; +import { apiSearchThings } from '@/services/master/ThingController'; +import { EditOutlined, EnvironmentOutlined } from '@ant-design/icons'; +import { + ActionType, + ProCard, + ProColumns, + ProTable, +} from '@ant-design/pro-components'; +import { FormattedMessage, useIntl } from '@umijs/max'; +import { Button, Divider, Grid, Space, Tag, theme } from 'antd'; +import message from 'antd/es/message'; +import Paragraph from 'antd/lib/typography/Paragraph'; +import { useRef, useState } from 'react'; +import CreateDevice from './components/CreateDevice'; +import EditDeviceModal from './components/EditDeviceModal'; + const ManagerDevicePage = () => { - return
ManagerDevicePage
; + const { useBreakpoint } = Grid; + const intl = useIntl(); + const screens = useBreakpoint(); + const { token } = theme.useToken(); + const actionRef = useRef(null); + const [isLoading, setIsLoading] = useState(false); + const [messageApi, contextHolder] = message.useMessage(); + const [selectedRowsState, setSelectedRowsState] = useState< + MasterModel.Thing[] + >([]); + const [groupCheckedKeys, setGroupCheckedKeys] = useState< + string | string[] | null + >(null); + const [isEditModalVisible, setIsEditModalVisible] = useState(false); + const [editingDevice, setEditingDevice] = useState( + null, + ); + + const handleClickAssign = (device: MasterModel.Thing) => { + console.log('Device ', device); + }; + + const handleEdit = (device: MasterModel.Thing) => { + setEditingDevice(device); + setIsEditModalVisible(true); + }; + + const handleEditCancel = () => { + setIsEditModalVisible(false); + setEditingDevice(null); + }; + + const handleEditSubmit = async (values: any) => { + // TODO: call update API here if available. For now just simulate success. + console.log('Update values for', editingDevice?.id, values); + messageApi.success('Cập nhật thành công'); + setIsEditModalVisible(false); + setEditingDevice(null); + actionRef.current?.reload(); + }; + + const columns: ProColumns[] = [ + { + key: 'name', + title: ( + + ), + tip: intl.formatMessage({ + id: 'master.devices.name.tip', + defaultMessage: 'The device name', + }), + dataIndex: 'name', + render: (_, record) => ( + + {record?.name} + + ), + }, + + { + key: 'external_id', + title: ( + + ), + tip: intl.formatMessage({ + id: 'master.devices.external_id.tip', + defaultMessage: 'The external identifier', + }), + responsive: ['lg', 'md'], + dataIndex: ['metadata', 'external_id'], + render: (_, record) => + record?.metadata?.external_id ? ( + + {record?.metadata?.external_id} + + ) : ( + '-' + ), + }, + + { + key: 'type', + hideInSearch: true, + title: ( + + ), + tip: intl.formatMessage({ + id: 'master.devices.type.tip', + defaultMessage: 'The device type', + }), + dataIndex: ['metadata', 'type'], + render: (_, record) => record?.metadata?.type || '...', + }, + + { + key: 'connected', + hideInSearch: true, + title: , + dataIndex: ['metadata', 'connected'], + render: (_, record) => ( + + {record?.metadata?.connected + ? intl.formatMessage({ + id: 'master.devices.online', + defaultMessage: 'Online', + }) + : intl.formatMessage({ + id: 'master.devices.offline', + defaultMessage: 'Offline', + })} + + ), + }, + + { + title: ( + + ), + hideInSearch: true, + render: (_, device) => { + return ( + } + > +