From 799bed4d36add1cb49ac969c26fee19691ddd9e6 Mon Sep 17 00:00:00 2001 From: Maxime Perrault Date: Wed, 29 Jan 2025 10:17:27 +0100 Subject: [PATCH] feat: add webworker and resolve timeout issue on loading images --- frontend/package-lock.json | 468 ++++++++++-------- frontend/package.json | 1 + .../Dashboard/components/Pdf/renderPdf.ts | 9 + .../Dashboard/components/Pdf/v1/Brief.tsx | 9 +- .../components/Pdf/v1/GeneratePdfButton.tsx | 69 ++- .../components/Pdf/v2/Amps/index.tsx | 2 +- .../Dashboard/components/Pdf/v2/Brief.tsx | 9 +- .../components/Pdf/v2/GeneratePdfButton.tsx | 77 +-- .../Pdf/v2/RegulatoryAreas/index.tsx | 2 +- .../components/Pdf/v2/Table/index.tsx | 2 +- .../Pdf/v2/VigilanceAreas/index.tsx | 2 +- .../Dashboard/components/Pdf/v2/utils.ts | 2 +- .../useExportImages.tsx} | 266 ++++------ frontend/src/features/Dashboard/types.ts | 2 +- frontend/src/features/Dashboard/utils.tsx | 69 ++- frontend/src/workers/MonitorEnvWebWorker.ts | 27 + frontend/src/workers/MonitorEnvWorker.ts | 6 + 17 files changed, 590 insertions(+), 432 deletions(-) create mode 100644 frontend/src/features/Dashboard/components/Pdf/renderPdf.ts rename frontend/src/features/Dashboard/{components/Layers/ExportLayer.tsx => hooks/useExportImages.tsx} (52%) create mode 100644 frontend/src/workers/MonitorEnvWebWorker.ts create mode 100644 frontend/src/workers/MonitorEnvWorker.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3fc3d8031..68e08db7e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -20,6 +20,7 @@ "@tanstack/react-table": "8.20.6", "@tanstack/react-virtual": "3.11.2", "classnames": "2.5.1", + "comlink": "4.4.2", "compressorjs": "1.2.1", "dayjs": "1.11.13", "deep-object-diff": "1.1.9", @@ -155,20 +156,20 @@ } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -416,23 +417,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", - "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "dependencies": { - "@babel/types": "^7.26.5" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -1530,11 +1531,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1544,9 +1545,9 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.5.tgz", - "integrity": "sha512-GJhPO0y8SD5EYVCy2Zr+9dSZcEgaSmq5BLR0Oc25TOEhC+ba49vUAGZFjy8v79z9E1mdldq4x9d1xgh4L1d5dQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.7.tgz", + "integrity": "sha512-5cJurntg+AT+cgelGP9Bt788DKiAw9gIMSMU2NJrLAilnj0m8WZWUNZPSLOmadYsujHutpgElO+50foX+ib/Wg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -1621,13 +1622,13 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", + "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1641,7 +1642,7 @@ "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.25.9", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1652,7 +1653,7 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.25.9", "@babel/plugin-transform-function-name": "^7.25.9", @@ -1661,12 +1662,12 @@ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -1683,7 +1684,7 @@ "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", @@ -1764,9 +1765,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.0.tgz", - "integrity": "sha512-YXHu5lN8kJCb1LOb9PgV6pvak43X2h4HvRApcN5SdWeaItQOzfn1hgP6jasD6KWQyJDBxrVmA9o9OivlnNJK/w==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.7.tgz", + "integrity": "sha512-55gRV8vGrCIYZnaQHQrD92Lo/hYE3Sj5tmbuf0hhHR7sj2CWhEhHU89hbq+UVDXvFG1zUVXJhUkEq1eAfqXtFw==", "dependencies": { "core-js-pure": "^3.30.2", "regenerator-runtime": "^0.14.0" @@ -1789,15 +1790,15 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", - "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.5", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1806,9 +1807,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", - "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -2426,14 +2427,14 @@ "dev": true }, "node_modules/@import-meta-env/cli": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@import-meta-env/cli/-/cli-0.7.2.tgz", - "integrity": "sha512-9Tob+l1K8a2ZS8jMZB9/MlsT5KAEiC3cZhRCNkbjw2Hd8L66pgUluUAwIout+/wnL64CRk7VDThiwpUSqz7sZQ==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@import-meta-env/cli/-/cli-0.7.3.tgz", + "integrity": "sha512-7xSPYhpXr0tulKk7Xv332fKRmoTwNUI+6eWUwgekNeRCNUvWsy9C0MfFk2rCDc43gGDJOywb1LxulthWpxFX1g==", "dev": true, "dependencies": { - "commander": "12.1.0", + "commander": "13.1.0", "dotenv": "^16.0.0", - "glob": "11.0.0", + "glob": "11.0.1", "picocolors": "1.1.1", "serialize-javascript": "6.0.2" }, @@ -2479,6 +2480,62 @@ "node": ">= 14" } }, + "node_modules/@import-meta-env/prepare/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@import-meta-env/prepare/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@import-meta-env/prepare/node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@import-meta-env/prepare/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@import-meta-env/unplugin": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/@import-meta-env/unplugin/-/unplugin-0.6.2.tgz", @@ -3863,9 +3920,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.1.tgz", + "integrity": "sha512-KeBYSwohb8g4/wCcnksvKTYlg69O62sQeLynn2YE+5z7JWEj95if27kclW9QqbrlsQ2DINI8fjbV3zyuKfwjKg==", "peer": true, "engines": { "node": ">=14.0.0" @@ -3906,9 +3963,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz", - "integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", + "integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==", "cpu": [ "arm" ], @@ -3919,9 +3976,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz", - "integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz", + "integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==", "cpu": [ "arm64" ], @@ -3932,9 +3989,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz", - "integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz", + "integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==", "cpu": [ "arm64" ], @@ -3945,9 +4002,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz", - "integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz", + "integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==", "cpu": [ "x64" ], @@ -3958,9 +4015,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz", - "integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz", + "integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==", "cpu": [ "arm64" ], @@ -3971,9 +4028,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz", - "integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz", + "integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==", "cpu": [ "x64" ], @@ -3984,9 +4041,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz", - "integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz", + "integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==", "cpu": [ "arm" ], @@ -3997,9 +4054,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz", - "integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz", + "integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==", "cpu": [ "arm" ], @@ -4010,9 +4067,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz", - "integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz", + "integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==", "cpu": [ "arm64" ], @@ -4023,9 +4080,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz", - "integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz", + "integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==", "cpu": [ "arm64" ], @@ -4036,9 +4093,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz", - "integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz", + "integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==", "cpu": [ "loong64" ], @@ -4049,9 +4106,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz", - "integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz", + "integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==", "cpu": [ "ppc64" ], @@ -4062,9 +4119,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz", - "integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz", + "integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==", "cpu": [ "riscv64" ], @@ -4075,9 +4132,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz", - "integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz", + "integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==", "cpu": [ "s390x" ], @@ -4088,9 +4145,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz", - "integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", + "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", "cpu": [ "x64" ], @@ -4101,9 +4158,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz", - "integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz", + "integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==", "cpu": [ "x64" ], @@ -4114,9 +4171,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz", - "integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz", + "integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==", "cpu": [ "arm64" ], @@ -4127,9 +4184,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz", - "integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz", + "integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==", "cpu": [ "ia32" ], @@ -4140,9 +4197,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz", - "integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz", + "integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==", "cpu": [ "x64" ], @@ -5358,9 +5415,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==" }, "node_modules/@types/mdast": { "version": "3.0.15", @@ -5722,9 +5779,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", - "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, "node_modules/@vitejs/plugin-react": { @@ -6398,6 +6455,15 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -7036,9 +7102,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001692", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", - "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", + "version": "1.0.30001696", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz", + "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", "funding": [ { "type": "opencollective", @@ -7376,9 +7442,9 @@ } }, "node_modules/color-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.0.1.tgz", - "integrity": "sha512-nKqUYlo0vZATVOFHY810BSYjmCARrG7e5R3UE3CQlyjJTvv5kSSmPG1kzm/oDyyqjehM+lW1RnEt9It9GNa5JA==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-space/-/color-space-2.1.1.tgz", + "integrity": "sha512-XbeoH4Zpc20jdDau6NOyJ0uVMsKgAGn6dLR+qS2i9wu5U5pvI1InK34r4ODCS3Gmm2Wjmz20nTfhwqED7rY8AQ==" }, "node_modules/color-string": { "version": "1.9.1", @@ -7405,6 +7471,11 @@ "node": ">= 0.8" } }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==" + }, "node_modules/comma-separated-tokens": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", @@ -7415,9 +7486,9 @@ } }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "engines": { "node": ">=18" @@ -8044,9 +8115,9 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "dev": true }, "node_modules/dedent": { @@ -8386,9 +8457,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.83", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz", - "integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==" + "version": "1.5.88", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.88.tgz", + "integrity": "sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==" }, "node_modules/emittery": { "version": "0.13.1", @@ -9897,9 +9968,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", - "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", "funding": [ { "type": "github", @@ -10155,12 +10226,18 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", + "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { @@ -10496,9 +10573,9 @@ } }, "node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", @@ -11149,11 +11226,12 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -12907,6 +12985,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/lint-staged/node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", @@ -14999,12 +15086,6 @@ } ] }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true - }, "node_modules/quick-lru": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", @@ -15280,13 +15361,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.28.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.1.tgz", - "integrity": "sha512-YraE27C/RdjcZwl5UCqF/ffXnZDxpJdk9Q6jw38SZHjXs7NNdpViq2l2c7fO7+4uWaEfcwfGCv3RSg4e1By/fQ==", + "version": "6.28.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.2.tgz", + "integrity": "sha512-O81EWqNJWqvlN/a7eTudAdQm0TbI7hw+WIi7OwwMcTn5JMyZ0ibTFNGz+t+Lju0df4LcqowCegcrK22lB1q9Kw==", "peer": true, "dependencies": { - "@remix-run/router": "1.21.0", - "react-router": "6.28.1" + "@remix-run/router": "1.21.1", + "react-router": "6.28.2" }, "engines": { "node": ">=14.0.0" @@ -15297,12 +15378,12 @@ } }, "node_modules/react-router-dom/node_modules/react-router": { - "version": "6.28.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.1.tgz", - "integrity": "sha512-2omQTA3rkMljmrvvo6WtewGdVh45SpL9hGiCI9uUrwGGfNFDIvGK4gYJsKlJoNVi6AQZcopSCballL+QGOm7fA==", + "version": "6.28.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.2.tgz", + "integrity": "sha512-BgFY7+wEGVjHCiqaj2XiUBQ1kkzfg6UoKYwEe0wv+FF+HNPCxtS/MVPvLAPH++EsuCMReZl9RYVGqcHLk5ms3A==", "peer": true, "dependencies": { - "@remix-run/router": "1.21.0" + "@remix-run/router": "1.21.1" }, "engines": { "node": ">=14.0.0" @@ -15790,9 +15871,9 @@ } }, "node_modules/rollup": { - "version": "4.30.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz", - "integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz", + "integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==", "dev": true, "dependencies": { "@types/estree": "1.0.6" @@ -15805,25 +15886,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.30.1", - "@rollup/rollup-android-arm64": "4.30.1", - "@rollup/rollup-darwin-arm64": "4.30.1", - "@rollup/rollup-darwin-x64": "4.30.1", - "@rollup/rollup-freebsd-arm64": "4.30.1", - "@rollup/rollup-freebsd-x64": "4.30.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.30.1", - "@rollup/rollup-linux-arm-musleabihf": "4.30.1", - "@rollup/rollup-linux-arm64-gnu": "4.30.1", - "@rollup/rollup-linux-arm64-musl": "4.30.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.30.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.30.1", - "@rollup/rollup-linux-riscv64-gnu": "4.30.1", - "@rollup/rollup-linux-s390x-gnu": "4.30.1", - "@rollup/rollup-linux-x64-gnu": "4.30.1", - "@rollup/rollup-linux-x64-musl": "4.30.1", - "@rollup/rollup-win32-arm64-msvc": "4.30.1", - "@rollup/rollup-win32-ia32-msvc": "4.30.1", - "@rollup/rollup-win32-x64-msvc": "4.30.1", + "@rollup/rollup-android-arm-eabi": "4.32.1", + "@rollup/rollup-android-arm64": "4.32.1", + "@rollup/rollup-darwin-arm64": "4.32.1", + "@rollup/rollup-darwin-x64": "4.32.1", + "@rollup/rollup-freebsd-arm64": "4.32.1", + "@rollup/rollup-freebsd-x64": "4.32.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.32.1", + "@rollup/rollup-linux-arm-musleabihf": "4.32.1", + "@rollup/rollup-linux-arm64-gnu": "4.32.1", + "@rollup/rollup-linux-arm64-musl": "4.32.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.32.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.32.1", + "@rollup/rollup-linux-riscv64-gnu": "4.32.1", + "@rollup/rollup-linux-s390x-gnu": "4.32.1", + "@rollup/rollup-linux-x64-gnu": "4.32.1", + "@rollup/rollup-linux-x64-musl": "4.32.1", + "@rollup/rollup-win32-arm64-msvc": "4.32.1", + "@rollup/rollup-win32-ia32-msvc": "4.32.1", + "@rollup/rollup-win32-x64-msvc": "4.32.1", "fsevents": "~2.3.2" } }, @@ -16566,13 +16647,12 @@ } }, "node_modules/streamx": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", - "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", "dev": true, "dependencies": { "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" }, "optionalDependencies": { @@ -17261,20 +17341,20 @@ "dev": true }, "node_modules/tldts": { - "version": "6.1.72", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.72.tgz", - "integrity": "sha512-QNtgIqSUb9o2CoUjX9T5TwaIvUUJFU1+12PJkgt42DFV2yf9J6549yTF2uGloQsJ/JOC8X+gIB81ind97hRiIQ==", + "version": "6.1.75", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.75.tgz", + "integrity": "sha512-+lFzEXhpl7JXgWYaXcB6DqTYXbUArvrWAE/5ioq/X3CdWLbDjpPP4XTrQBmEJ91y3xbe4Fkw7Lxv4P3GWeJaNg==", "dependencies": { - "tldts-core": "^6.1.72" + "tldts-core": "^6.1.75" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.72", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.72.tgz", - "integrity": "sha512-FW3H9aCaGTJ8l8RVCR3EX8GxsxDbQXuwetwwgXA2chYdsX+NY1ytCBl61narjjehWmCw92tc1AxlcY3668CU8g==" + "version": "6.1.75", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.75.tgz", + "integrity": "sha512-AOvV5YYIAFFBfransBzSTyztkc3IMfz5Eq3YluaRiEu55nn43Fzaufx70UqEKYr8BoLCach4q8g/bg6e5+/aFw==" }, "node_modules/tmp": { "version": "0.2.3", diff --git a/frontend/package.json b/frontend/package.json index 882948b57..de55ffd11 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,6 +37,7 @@ "@tanstack/react-table": "8.20.6", "@tanstack/react-virtual": "3.11.2", "classnames": "2.5.1", + "comlink": "4.4.2", "compressorjs": "1.2.1", "dayjs": "1.11.13", "deep-object-diff": "1.1.9", diff --git a/frontend/src/features/Dashboard/components/Pdf/renderPdf.ts b/frontend/src/features/Dashboard/components/Pdf/renderPdf.ts new file mode 100644 index 000000000..c7a058374 --- /dev/null +++ b/frontend/src/features/Dashboard/components/Pdf/renderPdf.ts @@ -0,0 +1,9 @@ +import { pdf } from '@react-pdf/renderer' +import { createElement } from 'react' + +import { Brief as BriefV1, type BriefProps as BriefPropsV1 } from './v1/Brief' +import { Brief, type BriefProps } from './v2/Brief' + +export const renderPDF = async (props: BriefProps) => pdf(createElement(Brief, props)).toBlob() + +export const renderPDFV1 = async (props: BriefPropsV1) => pdf(createElement(BriefV1, props)).toBlob() diff --git a/frontend/src/features/Dashboard/components/Pdf/v1/Brief.tsx b/frontend/src/features/Dashboard/components/Pdf/v1/Brief.tsx index b571fcd0c..69cd9a6c2 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v1/Brief.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v1/Brief.tsx @@ -16,15 +16,18 @@ import { VigilanceAreas } from './VigilanceAreas' import type { Dashboard } from '@features/Dashboard/types' -type BriefProps = { +export type BriefProps = { + author?: string brief: Dashboard.Brief + description?: string + title?: string } registerFonts() -export function Brief({ brief }: BriefProps) { +export function Brief({ author, brief, description, title }: BriefProps) { return ( - + diff --git a/frontend/src/features/Dashboard/components/Pdf/v1/GeneratePdfButton.tsx b/frontend/src/features/Dashboard/components/Pdf/v1/GeneratePdfButton.tsx index 936d59269..70f4ba767 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v1/GeneratePdfButton.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v1/GeneratePdfButton.tsx @@ -6,11 +6,9 @@ import { getVigilanceAreasByIds } from '@api/vigilanceAreasAPI' import { useAppSelector } from '@hooks/useAppSelector' import { useGetControlPlans } from '@hooks/useGetControlPlans' import { Button, Icon } from '@mtes-mct/monitor-ui' -import { usePDF } from '@react-pdf/renderer' import { useEffect, useMemo, useState } from 'react' import styled from 'styled-components' - -import { Brief } from './Brief' +import { MonitorEnvWebWorker } from 'workers/MonitorEnvWebWorker' import type { Dashboard } from '@features/Dashboard/types' @@ -19,7 +17,9 @@ type GeneratePdfButtonProps = { } export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { - const [isGenerating, setIsGenerating] = useState(false) + const [shouldTriggerExport, setShouldTriggerExport] = useState(false) + const [isOpening, setIsOpening] = useState(false) + const { subThemes, themes } = useGetControlPlans() const controlUnits = useAppSelector(state => getControlUnitsByIds(state, dashboard.controlUnitIds)) @@ -61,6 +61,8 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { vigilanceAreas }), [ + allLinkedAMPs, + allLinkedRegulatoryAreas, amps, dashboard.comments, dashboard.name, @@ -70,39 +72,52 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { reportings?.entities, subThemes, themes, - allLinkedAMPs, - vigilanceAreas, - allLinkedRegulatoryAreas + vigilanceAreas ] ) - const [pdf, update] = usePDF() - - const handleDownload = () => { - setIsGenerating(true) - - update() + const handleDownload = async () => { + setShouldTriggerExport(true) } useEffect(() => { - if (isGenerating && !pdf.loading && pdf.blob && pdf.url) { - setIsGenerating(false) + const renderPdf = async () => { + if (shouldTriggerExport) { + setIsOpening(true) + + const monitorEnvWorker = await MonitorEnvWebWorker + + const url = await monitorEnvWorker.renderPDFInWorkerV1({ brief }) + + if (url) { + const link = document.createElement('a') + link.href = url + link.download = `${dashboard.name}.pdf` + link.click() + link.remove() + URL.revokeObjectURL(url) + } + setShouldTriggerExport(false) + setIsOpening(false) + } + } + renderPdf() + }, [brief, dashboard.name, shouldTriggerExport]) - const link = document.createElement('a') - link.href = pdf.url - link.download = `${dashboard.name}.pdf` - link.click() + const getLoadingText = () => { + if (isOpening) { + return 'Chargement du brief' } - }, [isGenerating, pdf.loading, pdf.blob, pdf.url, dashboard.name]) + + return 'Générer un brief' + } return ( - - {pdf.loading || isGenerating ? 'Chargement du brief' : 'Générer un brief'} - + <> + + {getLoadingText()} + + ) } diff --git a/frontend/src/features/Dashboard/components/Pdf/v2/Amps/index.tsx b/frontend/src/features/Dashboard/components/Pdf/v2/Amps/index.tsx index d94f3e270..bc7f75e5c 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v2/Amps/index.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v2/Amps/index.tsx @@ -6,7 +6,7 @@ import { getTitle } from 'domain/entities/layers/utils' import { areaStyle, layoutStyle } from '../style' import { getImage } from '../utils' -import type { ExportImageType } from '../../../Layers/ExportLayer' +import type { ExportImageType } from '../../../../hooks/useExportImages' import type { AMPFromAPI } from 'domain/entities/AMPs' export function Amps({ amps, images }: { amps: AMPFromAPI[]; images: ExportImageType[] }) { diff --git a/frontend/src/features/Dashboard/components/Pdf/v2/Brief.tsx b/frontend/src/features/Dashboard/components/Pdf/v2/Brief.tsx index b40d14d9c..00f7ed029 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v2/Brief.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v2/Brief.tsx @@ -12,15 +12,18 @@ import { VigilanceAreas } from './VigilanceAreas' import type { Dashboard } from '@features/Dashboard/types' -type BriefProps = { +export type BriefProps = { + author?: string brief: Dashboard.Brief + description?: string + title?: string } registerFonts() -export function Brief({ brief }: BriefProps) { +export function Brief({ author, brief, description, title }: BriefProps) { return ( - + diff --git a/frontend/src/features/Dashboard/components/Pdf/v2/GeneratePdfButton.tsx b/frontend/src/features/Dashboard/components/Pdf/v2/GeneratePdfButton.tsx index 5fad8bcaa..94809b6d1 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v2/GeneratePdfButton.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v2/GeneratePdfButton.tsx @@ -3,16 +3,14 @@ import { getControlUnitsByIds } from '@api/controlUnitsAPI' import { getRegulatoryAreasByIds } from '@api/regulatoryLayersAPI' import { useGetReportingsByIdsQuery } from '@api/reportingsAPI' import { getVigilanceAreasByIds } from '@api/vigilanceAreasAPI' -import { useAppDispatch } from '@hooks/useAppDispatch' import { useAppSelector } from '@hooks/useAppSelector' import { useGetControlPlans } from '@hooks/useGetControlPlans' import { Button, Icon } from '@mtes-mct/monitor-ui' -import { usePDF } from '@react-pdf/renderer' import { useEffect, useMemo, useState } from 'react' import styled from 'styled-components' +import { MonitorEnvWebWorker } from 'workers/MonitorEnvWebWorker' -import { Brief } from './Brief' -import { ExportLayer, type ExportImageType } from '../../Layers/ExportLayer' +import { useExportImages } from '../../../hooks/useExportImages' import type { Dashboard } from '@features/Dashboard/types' @@ -21,10 +19,8 @@ type GeneratePdfButtonProps = { } export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { - const dispatch = useAppDispatch() - - const [isGenerating, setIsGenerating] = useState(false) - const [shouldLoadImage, setShouldLoadImage] = useState(false) + const [shouldTriggerExport, setShouldTriggerExport] = useState(false) + const [isOpening, setIsOpening] = useState(false) const { subThemes, themes } = useGetControlPlans() @@ -50,6 +46,8 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { const allLinkedAMPs = useAppSelector(state => getAmpsByIds(state, allLinkedAMPIds)) + const { images, loading } = useExportImages({ triggerExport: shouldTriggerExport }) + const brief: Dashboard.Brief = useMemo( () => ({ allLinkedAMPs, @@ -57,7 +55,7 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { amps, comments: dashboard.comments, controlUnits, - images: [], + images, name: dashboard.name, regulatoryAreas, reportings: Object.values(reportings?.entities ?? []), @@ -74,6 +72,7 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { dashboard.name, dashboard.updatedAt, controlUnits, + images, regulatoryAreas, reportings?.entities, subThemes, @@ -82,39 +81,53 @@ export function GeneratePdfButton({ dashboard }: GeneratePdfButtonProps) { ] ) - const [pdf, update] = usePDF() - - const handleDownload = () => { - setIsGenerating(true) - setShouldLoadImage(true) - } - - const updateBrief = (imagesToUpdate: ExportImageType[]) => { - update() - setShouldLoadImage(false) + const handleDownload = async () => { + setShouldTriggerExport(true) } useEffect(() => { - if (isGenerating && !shouldLoadImage && !pdf.loading && pdf.blob && pdf.url) { - setIsGenerating(false) - - const link = document.createElement('a') - link.href = pdf.url - link.download = `${dashboard.name}.pdf` - link.click() - link.remove() + const renderPdf = async () => { + if (brief.images && !loading && shouldTriggerExport) { + setIsOpening(true) + + const monitorEnvWorker = await MonitorEnvWebWorker + + const url = await monitorEnvWorker.renderPDFInWorker({ brief }) + + if (url) { + const link = document.createElement('a') + link.href = url + link.download = `${dashboard.name}.pdf` + link.click() + link.remove() + URL.revokeObjectURL(url) + } + setShouldTriggerExport(false) + setIsOpening(false) + } } - }, [dashboard.name, dispatch, isGenerating, pdf.blob, pdf.loading, pdf.url, shouldLoadImage]) + renderPdf() + }, [brief, dashboard.name, loading, shouldTriggerExport]) + + const getLoadingText = () => { + if (loading) { + return 'Chargement des images' + } + if (isOpening) { + return 'Chargement du brief' + } + + return 'Générer un brief' + } return ( <> - - {isGenerating ? 'Chargement du brief' : 'Générer un brief'} + {getLoadingText()} ) diff --git a/frontend/src/features/Dashboard/components/Pdf/v2/RegulatoryAreas/index.tsx b/frontend/src/features/Dashboard/components/Pdf/v2/RegulatoryAreas/index.tsx index 8c6913a30..2c146c6e4 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v2/RegulatoryAreas/index.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v2/RegulatoryAreas/index.tsx @@ -6,7 +6,7 @@ import { getTitle } from 'domain/entities/layers/utils' import { areaStyle, layoutStyle } from '../style' import { getImage } from '../utils' -import type { ExportImageType } from '../../../Layers/ExportLayer' +import type { ExportImageType } from '../../../../hooks/useExportImages' import type { RegulatoryLayerWithMetadata } from 'domain/entities/regulatory' export function RegulatoryAreas({ diff --git a/frontend/src/features/Dashboard/components/Pdf/v2/Table/index.tsx b/frontend/src/features/Dashboard/components/Pdf/v2/Table/index.tsx index 9b1c8b648..e2e509f37 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v2/Table/index.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v2/Table/index.tsx @@ -3,7 +3,7 @@ import { StyleSheet, Text, View, Image } from '@react-pdf/renderer' import { layoutStyle } from '../style' -import type { ExportImageType } from '../../../Layers/ExportLayer' +import type { ExportImageType } from '../../../../hooks/useExportImages' import type { VigilanceArea } from '@features/VigilanceArea/types' import type { AMPFromAPI } from 'domain/entities/AMPs' import type { RegulatoryLayerWithMetadata } from 'domain/entities/regulatory' diff --git a/frontend/src/features/Dashboard/components/Pdf/v2/VigilanceAreas/index.tsx b/frontend/src/features/Dashboard/components/Pdf/v2/VigilanceAreas/index.tsx index 998d52999..012863b15 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v2/VigilanceAreas/index.tsx +++ b/frontend/src/features/Dashboard/components/Pdf/v2/VigilanceAreas/index.tsx @@ -8,7 +8,7 @@ import { Image, Link, Text, View } from '@react-pdf/renderer' import { areaStyle, layoutStyle } from '../style' import { getImage } from '../utils' -import type { ExportImageType } from '../../../Layers/ExportLayer' +import type { ExportImageType } from '../../../../hooks/useExportImages' import type { AMPFromAPI } from 'domain/entities/AMPs' import type { RegulatoryLayerWithMetadata } from 'domain/entities/regulatory' diff --git a/frontend/src/features/Dashboard/components/Pdf/v2/utils.ts b/frontend/src/features/Dashboard/components/Pdf/v2/utils.ts index c61c890bc..70f6a9067 100644 --- a/frontend/src/features/Dashboard/components/Pdf/v2/utils.ts +++ b/frontend/src/features/Dashboard/components/Pdf/v2/utils.ts @@ -1,6 +1,6 @@ import { Dashboard } from '@features/Dashboard/types' -import type { ExportImageType } from '../../Layers/ExportLayer' +import type { ExportImageType } from '../../../hooks/useExportImages' export function getImage(images: ExportImageType[], type: Dashboard.Layer, id: number | undefined): string | undefined { return images.find(image => { diff --git a/frontend/src/features/Dashboard/components/Layers/ExportLayer.tsx b/frontend/src/features/Dashboard/hooks/useExportImages.tsx similarity index 52% rename from frontend/src/features/Dashboard/components/Layers/ExportLayer.tsx rename to frontend/src/features/Dashboard/hooks/useExportImages.tsx index 172f8c599..291efc13d 100644 --- a/frontend/src/features/Dashboard/components/Layers/ExportLayer.tsx +++ b/frontend/src/features/Dashboard/hooks/useExportImages.tsx @@ -4,12 +4,10 @@ import { useGetReportingsByIdsQuery } from '@api/reportingsAPI' import { useGetVigilanceAreasQuery } from '@api/vigilanceAreasAPI' import { getDashboardById } from '@features/Dashboard/slice' import { Dashboard } from '@features/Dashboard/types' +import { extractFeatures } from '@features/Dashboard/utils' import { CENTERED_ON_FRANCE } from '@features/map/BaseMap' -import { getAMPFeature } from '@features/map/layers/AMP/AMPGeometryHelpers' -import { getRegulatoryFeature } from '@features/map/layers/Regulatory/regulatoryGeometryHelpers' import { measurementStyle, measurementStyleWithCenter } from '@features/map/layers/styles/measurement.style' import { getReportingZoneFeature } from '@features/Reportings/components/ReportingLayer/Reporting/reportingsGeometryHelpers' -import { getVigilanceAreaZoneFeature } from '@features/VigilanceArea/components/VigilanceAreaLayer/vigilanceAreaGeometryHelper' import { useAppSelector } from '@hooks/useAppSelector' import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '@mtes-mct/monitor-ui' import { getFeature } from '@utils/getFeature' @@ -23,9 +21,9 @@ import { transform } from 'ol/proj' import { ImageTile } from 'ol/source' import VectorSource from 'ol/source/Vector' import { createXYZ } from 'ol/tilegrid' -import { useEffect, useRef, type MutableRefObject } from 'react' +import { useCallback, useEffect, useRef, useState, type MutableRefObject } from 'react' -import { getDashboardStyle } from './style' +import { getDashboardStyle } from '../components/Layers/style' import type { VectorLayerWithName } from 'domain/types/layer' import type { ImageLike } from 'ol/DataTile' @@ -76,11 +74,12 @@ export type ExportImageType = { } type ExportLayerProps = { - onImagesReady: (images: ExportImageType[]) => void - shouldLoadImages: boolean + triggerExport: boolean } -export function ExportLayer({ onImagesReady, shouldLoadImages }: ExportLayerProps) { +export function useExportImages({ triggerExport }: ExportLayerProps) { + const [images, setImages] = useState() + const [loading, setLoading] = useState(false) const mapRef = useRef(null) as MutableRefObject const activeDashboardId = useAppSelector(state => state.dashboard.activeDashboardId) @@ -118,15 +117,27 @@ export function ExportLayer({ onImagesReady, shouldLoadImages }: ExportLayerProp const view = inMemoryMap.getView() view.fit(extent, { - callback: () => { - setTimeout(() => { - resolve() - }, 300) - }, padding: [30, 30, 30, 200] }) + inMemoryMap.once('rendercomplete', () => { + resolve() + }) }) + const extractReportingFeatures = useCallback( + (allFeatures: Feature[]) => { + if (reportings) { + Object.values(reportings?.entities ?? []).forEach(reporting => { + if (reporting.geom) { + const feature = getReportingZoneFeature(reporting, Dashboard.featuresCode.DASHBOARD_REPORTINGS) + allFeatures.push(feature) + } + }) + } + }, + [reportings] + ) + useEffect(() => { const hiddenDiv = document.createElement('div') hiddenDiv.style.width = '1920px' @@ -142,187 +153,110 @@ export function ExportLayer({ onImagesReady, shouldLoadImages }: ExportLayerProp return () => { document.body.removeChild(hiddenDiv) + hiddenDiv.remove() } }, []) - useEffect(() => { - if (mapRef.current) { - mapRef.current.getLayers().push(layersVectorLayerRef.current) - } + const exportImages = useCallback( + async (features: Feature[], dashboardFeature?: Feature) => { + const allImages: ExportImageType[] = [] + const mapCanvas = mapRef.current?.getViewport().querySelector('canvas') + const mapContext = mapCanvas?.getContext('2d') - return () => { - if (mapRef.current) { - // eslint-disable-next-line react-hooks/exhaustive-deps - mapRef.current.removeLayer(layersVectorLayerRef.current) + if (!mapRef.current || !mapCanvas || !mapContext || !dashboardFeature) { + return allImages } - } - }, []) - useEffect(() => { - function extractRegulatoryAreaFeatures(allFeatures: Feature[]) { - if (regulatoryLayers?.entities) { - const regulatoryLayersIds = activeDashboard?.regulatoryAreaIds - regulatoryLayersIds?.forEach(layerId => { - const layer = regulatoryLayers.entities[layerId] - - if (layer?.geom && layer?.geom?.coordinates.length > 0) { - const feature = getRegulatoryFeature({ - code: Dashboard.featuresCode.DASHBOARD_REGULATORY_AREAS, - isolatedLayer: undefined, - layer + // eslint-disable-next-line no-restricted-syntax + for (const feature of features) { + mapContext.clearRect(0, 0, mapCanvas.width, mapCanvas.height) + layersVectorSourceRef.current.clear() + layersVectorSourceRef.current.addFeature(feature) + + dashboardFeature.setStyle([measurementStyle, measurementStyleWithCenter]) + layersVectorSourceRef.current.addFeature(dashboardFeature) + + // eslint-disable-next-line no-await-in-loop + await zoomToFeatures([dashboardFeature, feature]) + + mapRef.current + .getViewport() + .querySelectorAll('canvas') + .forEach(canvas => { + mapContext.drawImage(canvas, 0, 0) + allImages.push({ + featureId: feature.getId(), + image: mapCanvas.toDataURL('image/png') }) - if (feature) { - allFeatures.push(feature) - } - } - }) + }) } - } + extractReportingFeatures(features) - function extractAMPFeatures(allFeatures: Feature[]) { - if (ampLayers?.entities) { - const ampLayerIds = activeDashboard?.ampIds + layersVectorSourceRef.current.clear(true) - ampLayerIds?.forEach(layerId => { - const layer = ampLayers.entities[layerId] + layersVectorSourceRef.current.addFeatures([...features, dashboardFeature]) - if (layer?.geom && layer?.geom?.coordinates.length > 0) { - const feature = getAMPFeature({ - code: Dashboard.featuresCode.DASHBOARD_AMP, - isolatedLayer: undefined, - layer - }) + await zoomToFeatures([...features, dashboardFeature]) - if (feature) { - allFeatures.push(feature) - } - } + mapRef.current + ?.getTargetElement() + .querySelectorAll('canvas') + .forEach(canvas => { + mapContext?.drawImage(canvas, 0, 0) }) - } - } + allImages.push({ + featureId: 'WHOLE_DASHBOARD', + image: mapCanvas.toDataURL('image/png') + }) - function extractReportingFeatures(allFeatures: Feature[]) { - if (reportings) { - Object.values(reportings?.entities ?? []).forEach(reporting => { - if (reporting.geom) { - const feature = getReportingZoneFeature(reporting, Dashboard.featuresCode.DASHBOARD_REPORTINGS) - allFeatures.push(feature) - } - }) - } - } + return allImages + }, + [extractReportingFeatures] + ) - function extractVigilanceAreaFeatures(allFeatures: Feature[]) { - if (vigilanceAreas?.entities) { - const vigilanceAreaLayersIds = activeDashboard?.vigilanceAreaIds - vigilanceAreaLayersIds?.forEach(layerId => { - const layer = vigilanceAreas.entities[layerId] - if (layer?.geom && layer?.geom?.coordinates.length > 0) { - const feature = getVigilanceAreaZoneFeature( - layer, - Dashboard.featuresCode.DASHBOARD_VIGILANCE_AREAS, - undefined - ) - if (feature) { - allFeatures.push(feature) - } - } - }) - } + useEffect(() => { + if (mapRef.current) { + mapRef.current.getLayers().push(layersVectorLayerRef.current) } - if (mapRef.current && shouldLoadImages) { - // Exporter le canvas en image - const allImages: ExportImageType[] = [] - const allFeatures: Feature[] = [] - const mapCanvas = mapRef.current.getViewport().querySelector('canvas')! - const mapContext = mapCanvas.getContext('2d') - mapContext?.save() - let dashboardAreaFeature: Feature | undefined - - layersVectorSourceRef.current.clear(true) - - if (activeDashboard) { - extractRegulatoryAreaFeatures(allFeatures) - - extractAMPFeatures(allFeatures) - - extractVigilanceAreaFeatures(allFeatures) - } - - if (dashboard?.dashboard.geom) { - dashboardAreaFeature = getFeature(dashboard.dashboard.geom) - if (!dashboardAreaFeature) { - return - } - dashboardAreaFeature?.setStyle([measurementStyle, measurementStyleWithCenter]) + return () => { + if (mapRef.current) { + // eslint-disable-next-line react-hooks/exhaustive-deps + mapRef.current.removeLayer(layersVectorLayerRef.current) } + } + }, []) - const generateImages = async () => { - if (!mapRef.current || allFeatures.length === 0) { - onImagesReady([]) - } - - if (dashboardAreaFeature) { - // eslint-disable-next-line no-restricted-syntax - for (const feature of allFeatures) { - mapContext?.restore() - layersVectorSourceRef.current.clear(true) - layersVectorSourceRef.current.addFeature(feature) - - layersVectorSourceRef.current.addFeature(dashboardAreaFeature) - // eslint-disable-next-line no-await-in-loop - await zoomToFeatures([dashboardAreaFeature, feature]) - - mapRef.current - ?.getTargetElement() - .querySelectorAll('canvas') - .forEach(canvas => { - mapContext?.drawImage(canvas, 0, 0) - allImages.push({ - featureId: feature.getId(), - image: mapCanvas.toDataURL('image/png') - }) - }) - } - - extractReportingFeatures(allFeatures) - - layersVectorSourceRef.current.clear(true) - - layersVectorSourceRef.current.addFeatures([...allFeatures, dashboardAreaFeature]) - - await zoomToFeatures([...allFeatures, dashboardAreaFeature]) + useEffect(() => { + if (!mapRef.current || !triggerExport) { + setImages(undefined) - mapRef.current - ?.getTargetElement() - .querySelectorAll('canvas') - .forEach(canvas => { - mapContext?.drawImage(canvas, 0, 0) - }) - allImages.push({ - featureId: 'WHOLE_DASHBOARD', - image: mapCanvas.toDataURL('image/png') - }) - } + return + } - onImagesReady(allImages) - } + const features = extractFeatures(activeDashboard, regulatoryLayers, ampLayers, vigilanceAreas) + const dashboardFeature = dashboard?.dashboard.geom ? getFeature(dashboard.dashboard.geom) : undefined - generateImages() + const generateImages = async () => { + setLoading(true) + const allImages = await exportImages(features, dashboardFeature) + setImages(allImages) + setLoading(false) } + + generateImages() }, [ activeDashboard, - ampLayers?.entities, - dashboard?.dashboard.geom, - onImagesReady, - regulatoryLayers?.entities, + ampLayers, + regulatoryLayers, reportings, - shouldLoadImages, - vigilanceAreas?.entities + vigilanceAreas, + triggerExport, + dashboard?.dashboard.geom, + exportImages ]) - return null + return { images, loading } } function combineExtent(features: Feature[]): Extent { diff --git a/frontend/src/features/Dashboard/types.ts b/frontend/src/features/Dashboard/types.ts index 44ca64196..a0a55ec88 100644 --- a/frontend/src/features/Dashboard/types.ts +++ b/frontend/src/features/Dashboard/types.ts @@ -1,4 +1,4 @@ -import type { ExportImageType } from './components/Layers/ExportLayer' +import type { ExportImageType } from './hooks/useExportImages' import type { AMP, AMPFromAPI } from '../../domain/entities/AMPs' import type { RegulatoryLayerCompact, diff --git a/frontend/src/features/Dashboard/utils.tsx b/frontend/src/features/Dashboard/utils.tsx index 1ddd469a0..8d720580f 100644 --- a/frontend/src/features/Dashboard/utils.tsx +++ b/frontend/src/features/Dashboard/utils.tsx @@ -2,10 +2,19 @@ import { ampsAPI } from '@api/ampsAPI' import { regulatoryLayersAPI } from '@api/regulatoryLayersAPI' import { reportingsAPI } from '@api/reportingsAPI' import { vigilanceAreasAPI } from '@api/vigilanceAreasAPI' +import { getAMPFeature } from '@features/map/layers/AMP/AMPGeometryHelpers' +import { getRegulatoryFeature } from '@features/map/layers/Regulatory/regulatoryGeometryHelpers' +import { getVigilanceAreaZoneFeature } from '@features/VigilanceArea/components/VigilanceAreaLayer/vigilanceAreaGeometryHelper' import { isCypress } from '@utils/isCypress' -import type { Dashboard } from './types' +import { Dashboard } from './types' + +import type { VigilanceArea } from '@features/VigilanceArea/types' +import type { EntityState } from '@reduxjs/toolkit' import type { HomeRootState } from '@store/index' +import type { AMP } from 'domain/entities/AMPs' +import type { RegulatoryLayerWithMetadata } from 'domain/entities/regulatory' +import type { Feature } from 'ol' import type { Action } from 'redux' import type { ThunkDispatch } from 'redux-thunk' @@ -42,3 +51,61 @@ export async function populateExtractAreaFromApi( ) } } + +export const extractFeatures = ( + dashboard: Dashboard.Dashboard | undefined, + regulatoryLayers: EntityState | undefined, + ampLayers: EntityState | undefined, + vigilanceAreas: EntityState | undefined +) => { + const allFeatures: Feature[] = [] + + // Récupération des zones réglementaires + if (dashboard?.regulatoryAreaIds) { + dashboard.regulatoryAreaIds.forEach(layerId => { + const layer = regulatoryLayers?.entities[layerId] + if (layer?.geom?.coordinates.length) { + const feature = getRegulatoryFeature({ + code: Dashboard.featuresCode.DASHBOARD_REGULATORY_AREAS, + isolatedLayer: undefined, + layer + }) + if (feature) { + allFeatures.push(feature) + } + } + }) + } + + // AMP Features + if (dashboard?.ampIds) { + dashboard.ampIds.forEach(layerId => { + const layer = ampLayers?.entities[layerId] + if (layer?.geom?.coordinates.length) { + const feature = getAMPFeature({ + code: Dashboard.featuresCode.DASHBOARD_AMP, + isolatedLayer: undefined, + layer + }) + if (feature) { + allFeatures.push(feature) + } + } + }) + } + + // Zones de vigilance + if (dashboard?.vigilanceAreaIds) { + dashboard.vigilanceAreaIds.forEach(layerId => { + const layer = vigilanceAreas?.entities[layerId] + if (layer?.geom?.coordinates.length) { + const feature = getVigilanceAreaZoneFeature(layer, Dashboard.featuresCode.DASHBOARD_VIGILANCE_AREAS, undefined) + if (feature) { + allFeatures.push(feature) + } + } + }) + } + + return allFeatures +} diff --git a/frontend/src/workers/MonitorEnvWebWorker.ts b/frontend/src/workers/MonitorEnvWebWorker.ts new file mode 100644 index 000000000..d25b29633 --- /dev/null +++ b/frontend/src/workers/MonitorEnvWebWorker.ts @@ -0,0 +1,27 @@ +import * as Comlink from 'comlink' + +import { renderPDF, renderPDFV1 } from '../features/Dashboard/components/Pdf/renderPdf' + +import type { BriefProps as BriefPropsV1 } from '../features/Dashboard/components/Pdf/v1/Brief' +import type { BriefProps } from '../features/Dashboard/components/Pdf/v2/Brief' + +/** + * /!\ Do not shorten imports in the Web worker. + * It will fail the Vite build : `Rollup failed to resolve import [...]` + */ + +export class MonitorEnvWebWorker { + static renderPDFInWorker = async (props: BriefProps) => { + const blob = await renderPDF(props) + + return URL.createObjectURL(blob) + } + + static renderPDFInWorkerV1 = async (props: BriefPropsV1) => { + const blob = await renderPDFV1(props) + + return URL.createObjectURL(blob) + } +} + +Comlink.expose(MonitorEnvWebWorker) diff --git a/frontend/src/workers/MonitorEnvWorker.ts b/frontend/src/workers/MonitorEnvWorker.ts new file mode 100644 index 000000000..ec85dbb28 --- /dev/null +++ b/frontend/src/workers/MonitorEnvWorker.ts @@ -0,0 +1,6 @@ +import * as Comlink from 'comlink' + +import Worker from './MonitorEnvWorker?worker' + +const worker = new Worker() +export const MonitorEnvWorker = Comlink.wrap(worker)