diff --git a/apps/ops/.quasar/client-entry.js b/apps/ops/.quasar/client-entry.js index 8c0244d..a9a7b74 100644 --- a/apps/ops/.quasar/client-entry.js +++ b/apps/ops/.quasar/client-entry.js @@ -150,7 +150,11 @@ createQuasarApp(createApp, quasarUserOptions) return Promise[ method ]([ - import('boot/pinia') + import('boot/net-timeout'), + + import('boot/pinia'), + + import('boot/tooltip-ux') ]).then(bootFiles => { const boot = mapFn(bootFiles).filter(entry => typeof entry === 'function') diff --git a/apps/ops/.quasar/quasar-user-options.js b/apps/ops/.quasar/quasar-user-options.js index 8eb88f9..69ca7cb 100644 --- a/apps/ops/.quasar/quasar-user-options.js +++ b/apps/ops/.quasar/quasar-user-options.js @@ -17,5 +17,5 @@ import {Notify,Loading,LocalStorage,Dialog} from 'quasar' -export default { config: {},plugins: {Notify,Loading,LocalStorage,Dialog} } +export default { config: {"brand":{"primary":"#16a34a","positive":"#16a34a","negative":"#ef4444","warning":"#f59e0b","info":"#2563eb"}},plugins: {Notify,Loading,LocalStorage,Dialog} } diff --git a/apps/ops/knip.json b/apps/ops/knip.json new file mode 100644 index 0000000..1d5afba --- /dev/null +++ b/apps/ops/knip.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://unpkg.com/knip@latest/schema.json", + "paths": { "src/*": ["./src/*"] }, + "entry": [ + "quasar.config.js", + "src/App.vue", + "src/router/index.js", + "src/boot/*.js", + "src/modules/**/routes.js" + ], + "project": ["src/**/*.{js,vue}"], + "ignore": [".quasar/**", "src-pwa/**", "dist/**"], + "ignoreDependencies": [] +} diff --git a/apps/ops/package-lock.json b/apps/ops/package-lock.json index 4b0a620..f78eaaf 100644 --- a/apps/ops/package-lock.json +++ b/apps/ops/package-lock.json @@ -10,11 +10,15 @@ "dependencies": { "@quasar/extras": "^1.16.12", "@twilio/voice-sdk": "^2.18.1", + "@vue-flow/background": "^1.3.2", + "@vue-flow/controls": "^1.1.3", + "@vue-flow/core": "^1.48.2", "chart.js": "^4.5.1", "cytoscape": "^3.33.2", "grapesjs": "^0.22.16", "grapesjs-mjml": "^1.0.8", "grapesjs-preset-newsletter": "^1.0.2", + "hy-vue-gantt": "^5.2.1", "idb-keyval": "^6.2.1", "lucide-vue-next": "^1.0.0", "pinia": "^2.1.7", @@ -1611,7 +1615,6 @@ "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1777,6 +1780,79 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.2.0.tgz", + "integrity": "sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.2.0.tgz", + "integrity": "sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-7.2.0.tgz", + "integrity": "sha512-VNG8xqOip1JuJcC3zsVsKRQ60oXG9+oYNDCosjoU/H9pgYmLTEwWw8pE0jhPz/JWdHeUuK6+NQ3qsM4gIbdbYQ==", + "license": "(CC-BY-4.0 AND MIT)", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.2.0.tgz", + "integrity": "sha512-iycmlN51EULlQ4D/UU9WZnHiN0CvjJ2TuuCrAh+1MVdzD+4ViKYH2deNAll4XAAYlZa8WAefHR5taSK8hYmSMw==", + "license": "(CC-BY-4.0 AND MIT)", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.2.0.tgz", + "integrity": "sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w==", + "license": "(CC-BY-4.0 AND MIT)", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/vue-fontawesome": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.2.0.tgz", + "integrity": "sha512-7BwGjTZn8QDvVEIu8fvkHhsDRRv//tq7jtsldaDhF3dE1fyWLIQcEg3zvIzy33su7kcppWsZZ6XRYP5wp3UCgQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", + "vue": ">= 3.0.0 < 4" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -2643,6 +2719,12 @@ "sourcemap-codec": "^1.4.8" } }, + "node_modules/@tsconfig/node22": { + "version": "22.0.5", + "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.5.tgz", + "integrity": "sha512-hLf2ld+sYN/BtOJjHUWOk568dvjFQkHnLNa6zce25GIH+vxKfvTgm3qpaH6ToF5tu/NN0IH66s+Bb5wElHrLcw==", + "license": "MIT" + }, "node_modules/@twilio/voice-errors": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@twilio/voice-errors/-/voice-errors-1.7.0.tgz", @@ -2833,6 +2915,13 @@ "undici-types": "~7.18.0" } }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", + "license": "MIT", + "peer": true + }, "node_modules/@types/qs": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", @@ -2840,6 +2929,14 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", @@ -2894,7 +2991,7 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/underscore": { @@ -2903,6 +3000,13 @@ "integrity": "sha512-L6LBgy1f0EFQZ+7uSA57+n2g/s4Qs5r06Vwrwn0/nuK1de+adz00NWaztRQ30aEqw5qOaWbPI8u2cGQ52lj6VA==", "license": "MIT" }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT", + "peer": true + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -2930,6 +3034,84 @@ "vue": "^3.2.25" } }, + "node_modules/@vue-flow/background": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@vue-flow/background/-/background-1.3.2.tgz", + "integrity": "sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==", + "license": "MIT", + "peerDependencies": { + "@vue-flow/core": "^1.23.0", + "vue": "^3.3.0" + } + }, + "node_modules/@vue-flow/controls": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@vue-flow/controls/-/controls-1.1.3.tgz", + "integrity": "sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==", + "license": "MIT", + "peerDependencies": { + "@vue-flow/core": "^1.23.0", + "vue": "^3.3.0" + } + }, + "node_modules/@vue-flow/core": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@vue-flow/core/-/core-1.48.2.tgz", + "integrity": "sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==", + "license": "MIT", + "dependencies": { + "@vueuse/core": "^10.5.0", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + }, + "peerDependencies": { + "vue": "^3.3.0" + } + }, + "node_modules/@vue-flow/core/node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "license": "MIT" + }, + "node_modules/@vue-flow/core/node_modules/@vueuse/core": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz", + "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.1", + "@vueuse/shared": "10.11.1", + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vue-flow/core/node_modules/@vueuse/metadata": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz", + "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vue-flow/core/node_modules/@vueuse/shared": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz", + "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==", + "license": "MIT", + "dependencies": { + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@vue/compiler-core": { "version": "3.5.31", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.31.tgz", @@ -3048,6 +3230,47 @@ "integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==", "license": "MIT" }, + "node_modules/@vueuse/core": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz", + "integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.3.0", + "@vueuse/shared": "14.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.3.0.tgz", + "integrity": "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.3.0.tgz", + "integrity": "sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3266,7 +3489,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { @@ -3325,6 +3547,16 @@ "node": ">=8" } }, + "node_modules/astronomia": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/astronomia/-/astronomia-4.2.0.tgz", + "integrity": "sha512-mTvpBGyXB80aSsDhAAiuwza5VqAyqmj5yzhjBrFhRy17DcWDzJrb8Vdl4Sm+g276S+mY7bk/5hi6akZ5RQFeHg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -3447,6 +3679,16 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3661,6 +3903,19 @@ "node": ">= 0.8" } }, + "node_modules/caldate": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/caldate/-/caldate-2.0.5.tgz", + "integrity": "sha512-JndhrUuDuE975KUhFqJaVR1OQkCHZqpOrJur/CFXEIEhWhBMjxO85cRSK8q4FW+B+yyPq6GYua2u4KvNzTcq0w==", + "license": "ISC", + "peer": true, + "dependencies": { + "moment-timezone": "^0.5.43" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -3753,6 +4008,27 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4091,6 +4367,19 @@ "dev": true, "license": "MIT" }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.49.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", @@ -4164,6 +4453,16 @@ "node": ">=8" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "peer": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4192,6 +4491,111 @@ "node": ">=0.10" } }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -4246,6 +4650,85 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-bengali-revised": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/date-bengali-revised/-/date-bengali-revised-2.0.2.tgz", + "integrity": "sha512-q9iDru4+TSA9k4zfm0CFHJj6nBsxP7AYgWC/qodK/i7oOIlj5K2z5IcQDtESfs/Qwqt/xJYaP86tkazd/vRptg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/date-chinese": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/date-chinese/-/date-chinese-2.1.4.tgz", + "integrity": "sha512-WY+6+Qw92ZGWFvGtStmNQHEYpNa87b8IAQ5T8VKt4wqrn24lBXyyBnWI5jAIyy7h/KVwJZ06bD8l/b7yss82Ww==", + "license": "MIT", + "peer": true, + "dependencies": { + "astronomia": "^4.1.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/date-easter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/date-easter/-/date-easter-1.0.3.tgz", + "integrity": "sha512-aOViyIgpM4W0OWUiLqivznwTtuMlD/rdUWhc5IatYnplhPiWrLv75cnifaKYhmQwUBLAMWLNG4/9mlLIbXoGBQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/date-holidays": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/date-holidays/-/date-holidays-3.30.2.tgz", + "integrity": "sha512-B7GLBc/7ucEw4KZeRKRMZin1NlTbQJtXn4tW4lHoRKdRzB68A/O6s/Jxlr/zStkZv43IeAQivYii/0dJytikGw==", + "license": "(ISC AND CC-BY-3.0)", + "peer": true, + "dependencies": { + "date-holidays-parser": "^3.4.7", + "js-yaml": "^4.1.1", + "lodash": "^4.18.1", + "prepin": "^1.0.3" + }, + "bin": { + "holidays2json": "scripts/holidays2json.cjs" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/date-holidays-parser": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/date-holidays-parser/-/date-holidays-parser-3.4.7.tgz", + "integrity": "sha512-h09ZEtM6u5cYM6m1bX+1Ny9f+nLO9KVZUKNPEnH7lhbXYTfqZogaGTnhONswGeIJFF91UImIftS3CdM9HLW5oQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "astronomia": "^4.1.1", + "caldate": "^2.0.5", + "date-bengali-revised": "^2.0.2", + "date-chinese": "^2.1.4", + "date-easter": "^1.0.3", + "deepmerge": "^4.3.1", + "jalaali-js": "^1.2.7", + "moment-timezone": "^0.5.47" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT", + "peer": true + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4267,7 +4750,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4377,6 +4859,17 @@ "node": ">=6.0.0" } }, + "node_modules/dompurify": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true, + "peer": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -5366,6 +5859,18 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -5393,6 +5898,13 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "license": "MIT", + "peer": true + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -6010,6 +6522,20 @@ "node": "^14.13.1 || >=16.0.0" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -6031,6 +6557,31 @@ "url": "https://opencollective.com/express" } }, + "node_modules/hy-vue-gantt": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hy-vue-gantt/-/hy-vue-gantt-5.2.1.tgz", + "integrity": "sha512-b6UfjHWvsMtJ6UTm+kemPlsuEAPEbxy65eM8DgIEs1U22asPOQIMhmIJIuvE2m9IXK1rTeVqlJoW+Ggi9+NI0A==", + "license": "MIT", + "dependencies": { + "@tsconfig/node22": "^22.0.0" + }, + "peerDependencies": { + "@fortawesome/free-brands-svg-icons": "^7.1.0", + "@fortawesome/free-regular-svg-icons": "^7.1.0", + "@fortawesome/free-solid-svg-icons": "^7.1.0", + "@fortawesome/vue-fontawesome": "^3.0.8", + "@vueuse/core": "^14.2.0", + "date-holidays": "^3.23.14", + "dayjs": "^1.11.13", + "html2canvas": "^1.4.1", + "jspdf": "^4.1.0", + "lodash-es": "^4.17.21", + "papaparse": "^5.5.2", + "uuid": "^13.0.0", + "vue": "^3.5.13", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -6183,6 +6734,13 @@ "node": ">= 0.4" } }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", + "license": "MIT", + "peer": true + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6778,6 +7336,13 @@ "node": ">=10" } }, + "node_modules/jalaali-js": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.8.tgz", + "integrity": "sha512-Jl/EwY84JwjW2wsWqeU4pNd22VNQ7EkjI36bDuLw31wH98WQW4fPjD0+mG7cdCK+Y8D6s9R3zLiQ3LaKu6bD8A==", + "license": "MIT", + "peer": true + }, "node_modules/jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -6804,7 +7369,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -6883,6 +7447,24 @@ "node": ">=0.10.0" } }, + "node_modules/jspdf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", + "integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.28.6", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0-rc.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7004,12 +7586,18 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true, + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT", + "peer": true + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -7297,6 +7885,29 @@ "integrity": "sha512-Y3kpr3IFBk3Wm7AwONZ5vDAX7FxAaMk+RKbcKKewsuGI9oNCOSM2dWNcWVFdzZ9PF7awoaCgBSQudnJaJbUiBA==", "license": "MIT" }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "peer": true, + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -7598,6 +8209,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)", + "peer": true + }, + "node_modules/papaparse": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.4.tgz", + "integrity": "sha512-SwzWD9gl/ElwYLCI0nUja1mFJzjq2D8ziShfNBa7zCHzkOozeOGDwHWQ+tvCzEZcewecWZ5U7kUopDnG+DFYEQ==", + "license": "MIT", + "peer": true + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -7687,6 +8312,14 @@ "dev": true, "license": "MIT" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7790,6 +8423,16 @@ "node": ">= 0.8.0" } }, + "node_modules/prepin": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/prepin/-/prepin-1.0.3.tgz", + "integrity": "sha512-0XL2hreherEEvUy0fiaGEfN/ioXFV+JpImqIzQjxk6iBg4jQ2ARKqvC4+BmRD8w/pnpD+lbxvh0Ub+z7yBEjvA==", + "license": "Unlicense", + "peer": true, + "bin": { + "prepin": "bin/prepin.js" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -7892,6 +8535,17 @@ ], "license": "MIT" }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8032,6 +8686,14 @@ "node": ">=4" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -8184,6 +8846,17 @@ "node": ">=0.10.0" } }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -8810,6 +9483,17 @@ "node": ">=16" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -9033,6 +9717,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/table": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", @@ -9159,6 +9854,16 @@ "dev": true, "license": "MIT" }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "peer": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -9508,6 +10213,30 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "peer": true, + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/uuid": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", + "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "peer": true, + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10168,6 +10897,19 @@ "dev": true, "license": "ISC" }, + "node_modules/xlsx": { + "version": "0.20.2", + "resolved": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz", + "integrity": "sha512-+nKZ39+nvK7Qq6i0PvWWRA4j/EkfWOtkP/YhMtupm+lJIiHxUrgTr1CcKv1nBk1rHtkRRQ3O2+Ih/q/sA+FXZA==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", diff --git a/apps/ops/package.json b/apps/ops/package.json index bfec0b7..730469c 100644 --- a/apps/ops/package.json +++ b/apps/ops/package.json @@ -7,16 +7,21 @@ "scripts": { "dev": "quasar dev", "build": "quasar build -m pwa", - "lint": "eslint --ext .js,.vue ./src" + "lint": "eslint --ext .js,.vue ./src", + "knip": "npx --yes knip" }, "dependencies": { "@quasar/extras": "^1.16.12", "@twilio/voice-sdk": "^2.18.1", + "@vue-flow/background": "^1.3.2", + "@vue-flow/controls": "^1.1.3", + "@vue-flow/core": "^1.48.2", "chart.js": "^4.5.1", "cytoscape": "^3.33.2", "grapesjs": "^0.22.16", "grapesjs-mjml": "^1.0.8", "grapesjs-preset-newsletter": "^1.0.2", + "hy-vue-gantt": "^5.2.1", "idb-keyval": "^6.2.1", "lucide-vue-next": "^1.0.0", "pinia": "^2.1.7", diff --git a/apps/ops/quasar.config.js b/apps/ops/quasar.config.js index a6902da..5cf6d74 100644 --- a/apps/ops/quasar.config.js +++ b/apps/ops/quasar.config.js @@ -1,9 +1,17 @@ /* eslint-env node */ const { configure } = require('quasar/wrappers') +// ⚠️ HARNAIS D'APERÇU LOCAL UNIQUEMENT — À RETIRER AVANT TOUT BUILD PROD. +// Lit le jeton hub depuis .env.local (gitignored) et l'injecte CÔTÉ PROXY (jamais dans le bundle client). +let _HUB_TOKEN = '' +try { + const _m = require('fs').readFileSync(__dirname + '/.env.local', 'utf8').match(/^HUB_TOKEN=(.+)$/m) + _HUB_TOKEN = _m ? _m[1].trim() : '' +} catch (e) { /* pas de .env.local → proxy /hub inactif */ } + module.exports = configure(function () { return { - boot: ['pinia', 'tooltip-ux'], + boot: ['net-timeout', 'pinia', 'tooltip-ux'], css: ['app.scss', 'tech.scss'], @@ -34,11 +42,20 @@ module.exports = configure(function () { target: 'https://erp.gigafibre.ca', changeOrigin: true, }, + // ⚠️ HARNAIS APERÇU LOCAL — /hub → hub live, jeton injecté côté proxy (jamais bundlé). À RETIRER avant build prod. + '/hub': { + target: 'https://msg.gigafibre.ca', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/hub/, ''), + headers: { Authorization: 'Bearer ' + _HUB_TOKEN, 'X-Authentik-Email': 'louis@targo.ca' }, + }, }, }, framework: { - config: {}, + // Aligne les couleurs Quasar sur la palette de marque (vert). primary = accent (--ops-accent). + // Après migration color="indigo-6/7"→"primary", tout l'accent devient vert d'un coup. + config: { brand: { primary: '#16a34a', positive: '#16a34a', negative: '#ef4444', warning: '#f59e0b', info: '#2563eb' } }, plugins: ['Notify', 'Loading', 'LocalStorage', 'Dialog'], }, diff --git a/apps/ops/src/App.vue b/apps/ops/src/App.vue index 7471cee..f73455e 100644 --- a/apps/ops/src/App.vue +++ b/apps/ops/src/App.vue @@ -5,7 +5,13 @@ diff --git a/apps/ops/src/api/auth.js b/apps/ops/src/api/auth.js index 33c797b..cb62f23 100644 --- a/apps/ops/src/api/auth.js +++ b/apps/ops/src/api/auth.js @@ -7,6 +7,11 @@ const SERVICE_TOKEN = import.meta.env.VITE_ERP_TOKEN || '' // Reconnexion auto sur session Authentik expirée, anti-boucle (1 reload / 20 s max). let _lastReload = 0 function _reauth (status) { + // DEV local (pas d'Authentik devant) : un 401 ERPNext ne doit PAS déclencher un rechargement en boucle. + // On renvoie le 401 et l'UI affiche ses états vides. En prod (Authentik) le comportement est inchangé. + if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + return new Response('{}', { status: status || 401 }) + } const now = Date.now() if (now - _lastReload > 20000) { _lastReload = now @@ -24,18 +29,23 @@ export async function authFetch (url, opts = {}) { try { res = await fetch(url, opts) } catch (e) { - // Coupure réseau transitoire (ex: backend qui redémarre) → 1 retry court avant d'abandonner + // Coupure réseau transitoire (backend qui redémarre) OU timeout du garde net (session lente) → 1 retry court await new Promise(r => setTimeout(r, 800)) - res = await fetch(url, opts) + try { + res = await fetch(url, opts) + } catch (e2) { + // 2e échec (réseau/timeout) : NE PAS planter l'appelant ni boucler sur reload → réponse vide, l'app reste réactive + return new Response('{}', { status: 504 }) + } } // Détection « session Authentik expirée » (la cause du "il faut recharger") : // - 401/403 explicites // - réponse redirigée vers l'IdP (auth.targo.ca / /if/flow/) - // - page HTML de login renvoyée à la place du JSON attendu sur un /api/ + // - page HTML de login renvoyée à la place du JSON attendu sur un endpoint app (/api, /auth, /hub) const ct = (res.headers && res.headers.get && res.headers.get('content-type')) || '' const redirectedToIdp = res.redirected && /auth\.targo\.ca|\/if\/flow\//.test(res.url || '') - const htmlForApi = res.status === 200 && ct.includes('text/html') && /\/api\//.test(String(url)) + const htmlForApi = res.status === 200 && ct.includes('text/html') && /\/(api|auth|hub)\//.test(String(url)) if (res.status === 401 || res.status === 403 || redirectedToIdp || htmlForApi) { return _reauth(res.status) } @@ -43,6 +53,8 @@ export async function authFetch (url, opts = {}) { } export async function getLoggedUser () { + // ⚠️ APERÇU LOCAL UNIQUEMENT : identité forcée via VITE_DEV_USER (gardé localhost + env). Inerte en prod. À RETIRER avec le harnais. + if ((window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') && import.meta.env.VITE_DEV_USER) return import.meta.env.VITE_DEV_USER // First try nginx whoami endpoint (returns Authentik email header) try { const res = await fetch(BASE_URL + '/auth/whoami') diff --git a/apps/ops/src/api/billing.js b/apps/ops/src/api/billing.js new file mode 100644 index 0000000..ace3602 --- /dev/null +++ b/apps/ops/src/api/billing.js @@ -0,0 +1,35 @@ +/** + * api/billing.js — Client for Hub /billing approval endpoints (P1). + * + * Porte d'approbation de facturation : lister les installations complétées en + * attente, prévisualiser le prorata (LECTURE SEULE), approuver (= active le + * service ; la facturation reste gatée côté hub par PRORATION_WRITE). + */ +import { HUB_URL } from 'src/config/hub' + +async function hubFetch (path, { method = 'GET', body } = {}) { + const opts = { method, headers: { 'Content-Type': 'application/json' } } + if (body) opts.body = JSON.stringify(body) + const res = await fetch(`${HUB_URL}${path}`, opts) + const text = await res.text() + let data + try { data = text ? JSON.parse(text) : {} } catch { throw new Error(`Invalid JSON from ${path}: ${text.slice(0, 200)}`) } + if (!res.ok) { const err = new Error(data.error || `HTTP ${res.status}`); err.status = res.status; throw err } + return data +} + +// Activations en attente d'approbation (enrichies client + adresse). +export function getApprovals () { + return hubFetch('/billing/approvals').then(r => r.pending || []) +} + +// Aperçu prorata consolidé pour un job (LECTURE SEULE — aucune facture créée). +export function getApprovalPreview (job) { + return hubFetch(`/billing/approvals/preview?job=${encodeURIComponent(job)}`) +} + +// Approuve → active les abonnements. L'approbateur est attribué côté serveur +// (en-tête x-authentik-email). La facturation reste gatée (PRORATION_WRITE). +export function approveActivation (job) { + return hubFetch('/billing/approvals/approve', { method: 'POST', body: { job } }) +} diff --git a/apps/ops/src/api/campaigns.js b/apps/ops/src/api/campaigns.js index 967d78b..d054663 100644 --- a/apps/ops/src/api/campaigns.js +++ b/apps/ops/src/api/campaigns.js @@ -92,6 +92,37 @@ export function listGifts () { return hubFetch('/campaigns/gifts').then(r => r.gifts || []) } +// Pool de liens-cadeaux récupérés (jamais cliqués) prêts à réallouer à d'autres clients. +export function getGiftPool () { + return hubFetch('/campaigns/gifts/pool') +} + +// Récupère les liens non cliqués d'une campagne vers le pool (dry_run par défaut = aperçu). +export function reclaimGifts ({ campaign_id, dry_run = true, revoke_live = true }) { + return hubFetch('/campaigns/gifts/reclaim', { method: 'POST', body: { campaign_id, dry_run, revoke_live } }) +} + +// Envoi unitaire d'une récompense depuis le pool vers un client (dry_run pour aperçu). +export function rewardSend (body) { + return hubFetch('/campaigns/reward-send', { method: 'POST', body }) +} + +// Récompenses déjà préparées/envoyées à un client (pour la fiche : voir / copier le lien). +export function rewardByCustomer (customer_id, email) { + const q = customer_id ? 'customer_id=' + encodeURIComponent(customer_id) : 'email=' + encodeURIComponent(email || '') + return hubFetch('/campaigns/reward/by-customer?' + q) +} + +// Supprime un brouillon / révoque une récompense non cliquée (remet le lien en réserve ; refusé si déjà ouverte). +export function revokeRewardByToken (gift_token) { + return hubFetch('/campaigns/reward/revoke', { method: 'POST', body: { gift_token } }) +} + +// Révoque une récompense déjà envoyée + remet le lien Giftbit au pool (refusé si déjà cliqué/redirigé). +export function revokeReward (gift_url) { + return hubFetch('/campaigns/gifts/revoke-reward', { method: 'POST', body: { gift_url } }) +} + // Kill switch — manually expire a single recipient's wrapper token so // the underlying Giftbit URL becomes reassignable before the natural // expiry date. diff --git a/apps/ops/src/api/reports.js b/apps/ops/src/api/reports.js index 82ee17c..afc5673 100644 --- a/apps/ops/src/api/reports.js +++ b/apps/ops/src/api/reports.js @@ -91,3 +91,23 @@ export function overpricedInternetCsvUrl ({ threshold = 90, segment = 'residenti }) return `${HUB}/reports/legacy/overpriced-internet.csv?${qs}` } + +/** + * Explorateur de revenus — MRR net récurrent regroupé par DIMENSION (un seul rapport, + * critères ajoutables). Remplace les rapports F fixes (par service / code postal / ville / segment). + * @param {object} opts { dimension:'category|zip|city|segment|plan', segment, type:'internet|tv|phone|all', active, threshold, limit } + */ +export function fetchRevenueExplorer ({ dimension = 'category', segment = 'all', type = 'all', active = true, threshold = 0, limit = 300 } = {}) { + const qs = new URLSearchParams({ + dimension, segment, type, active: active ? '1' : '0', threshold: String(threshold), limit: String(limit), + }) + return hubFetch(`/reports/legacy/revenue-explorer?${qs}`) +} + +/** URL CSV directe pour l'explorateur de revenus (mêmes critères). */ +export function revenueExplorerCsvUrl ({ dimension = 'category', segment = 'all', type = 'all', active = true, threshold = 0 } = {}) { + const qs = new URLSearchParams({ + dimension, segment, type, active: active ? '1' : '0', threshold: String(threshold), + }) + return `${HUB}/reports/legacy/revenue-explorer.csv?${qs}` +} diff --git a/apps/ops/src/api/roster.js b/apps/ops/src/api/roster.js index 621fef5..9c3e510 100644 --- a/apps/ops/src/api/roster.js +++ b/apps/ops/src/api/roster.js @@ -28,6 +28,10 @@ async function jput (path, body) { export const listTechnicians = () => jget('/roster/technicians') export const listTemplates = () => jget('/roster/templates') export const createTemplate = (t) => jpost('/roster/templates', t) +// Fériés QC déterministes (calendrier) + préavis de disponibilité avant un férié. +export const holidays = (from, to) => jget(`/roster/holidays?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`) +export const holidayNotice = (days = 35) => jget('/roster/holiday-notice?days=' + days) +export const sendHolidayNotice = (date) => jpost('/roster/holiday-notice', { date }) export const listRequirements = (start, days = 7) => jget(`/roster/requirements?start=${start}&days=${days}`) export const createRequirement = (r) => jpost('/roster/requirements', r) export const listAssignments = (start, days = 7) => jget(`/roster/assignments?start=${start}&days=${days}`) @@ -98,6 +102,20 @@ export const pushLegacyPreview = () => jget('/dispatch/legacy-sync/push-assignme export const pushLegacyApply = (notify = true) => jpost('/dispatch/legacy-sync/push-assignments' + (notify ? '' : '?notify=0'), {}) // Fil complet d'un ticket legacy (osTicket) : messages + réponses, pour l'expand au clic export const ticketThread = (id) => jget('/dispatch/legacy-sync/ticket-thread?id=' + encodeURIComponent(id)) +// Poster au fil d'un ticket : isPublic=false (défaut) = note INTERNE (jamais au client) · true = message public +export const postTicket = (ticket, msg, isPublic) => jpost('/dispatch/legacy-sync/ticket-post', { ticket, msg, public: !!isPublic }) +// GÉNÉRALISÉ par JOB (osTicket OU ERPNext-natif) : fil+contact (lecture seule) et résolution de conversation pour répondre. +export const jobThread = (job) => jget('/dispatch/legacy-sync/job-thread?job=' + encodeURIComponent(job)) +export const jobConversation = (job) => jget('/dispatch/legacy-sync/job-conversation?job=' + encodeURIComponent(job)) +// P2 — réponse au CLIENT via le MÊME chemin que la Boîte (/conversations/{token}/messages → Outbox courriel/SMS + miroir osTicket). +// X-Authentik-Email REQUIS : sans lui le hub enregistre l'envoi comme message CLIENT (from='customer') + déclenche l'IA. À passer (useAuthStore().user). +export async function sendConvMessage (token, text, agentEmail) { + const headers = { 'Content-Type': 'application/json' } + if (agentEmail) headers['X-Authentik-Email'] = agentEmail + const r = await fetch(HUB + '/conversations/' + encodeURIComponent(token) + '/messages', { method: 'POST', headers, body: JSON.stringify({ text }) }) + if (!r.ok) throw new Error('Envoi conv ' + r.status) + return r.json() +} // Fermer un ticket dans le legacy (status=closed + date_closed + closed_by=acteur + réouverture enfants) export const closeLegacyTicket = (ticket) => jpost('/dispatch/legacy-sync/close-ticket?ticket=' + encodeURIComponent(ticket), {}) // Fermeture EN LOT (1 appel pour N tickets) — efficace @@ -112,6 +130,10 @@ export const reorderJobs = (updates) => jpost('/roster/reorder-jobs', { updates export const unassignJobRoster = (job) => jpost('/roster/unassign-job', { job }) // Situer manuellement un job « hors carte » : pose latitude/longitude (+ adresse) choisis sur la carte export const setJobLocation = (job, lat, lon, address) => jpost('/roster/job/set-location', { job, lat, lon, address }) +// Actions rapides du pool (mobile) : patch d'un Dispatch Job (priority/status/required_skill/scheduled_date/notes — liste blanche côté hub). +export const updateJob = (job, patch) => jpost('/roster/job/update', { job, patch }) +// Persister UN quart immédiatement (sinon addShift reste local/Proposé et disparaît au rechargement). Idempotent côté hub. +export const createShift = (s) => jpost('/roster/shift', s) // ÉQUIPE d'un job (assistants en renfort) — le lead reste inchangé ; l'assistant épinglé voit un bloc hachuré dans son horaire. export const getJobTeam = (job) => jget('/roster/job/team?job=' + encodeURIComponent(job)) export const addAssistant = (job, a) => jpost('/roster/job/team', { job, add: a }) @@ -139,3 +161,14 @@ export const notifyReschedule = (body) => jpost('/roster/job/notify-reschedule', export const gpxUrl = (tech, from, to) => HUB + '/traccar/gpx?tech=' + encodeURIComponent(tech) + '&from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to) // Tracé GPS (breadcrumb) d'un tech sur UNE journée → { coords:[[lon,lat]…], count }. Plage exacte (1 jour) obligatoire (Traccar lourd sinon). export const traccarTrack = (tech, from, to) => jget('/traccar/track?tech=' + encodeURIComponent(tech) + '&from=' + encodeURIComponent(from) + '&to=' + encodeURIComponent(to)) +// Validation « service livré » d'un job : statut service+modem (en ligne + signal) pour le client/adresse du job. +// Croise tech-présent (geofence) + service-en-ligne → preuve de complétion d'install. +export const jobServiceStatus = (job) => jget('/roster/job-service-status?job=' + encodeURIComponent(job)) +// Aperçu croisement temps-sur-site (Traccar × emplacement job) + ancre d'activation GenieACS (clamped_minutes / service_anchor). +export const onsiteCross = (days = 30) => jget('/traccar/onsite?days=' + days) +// Position GPS LIVE (dernière connue) d'un tech → { ok, lat, lon, time, speed } ; pour fixer son point de départ (domicile). +export const techLivePosition = (tech) => jget('/traccar/live?tech=' + encodeURIComponent(tech)) +// Liste des appareils Traccar (pour associer un tech à son GPS) — { id, name, uniqueId, status, lastUpdate }. +export const listTraccarDevices = () => jget('/traccar/devices') +// Associer / changer (device_id) ou dissocier ('') l'appareil Traccar d'un tech — INLINE depuis Planif. +export const setTechTraccarDevice = (id, deviceId) => jpost('/roster/technician/' + encodeURIComponent(id) + '/traccar-device', { device_id: deviceId }) diff --git a/apps/ops/src/api/subcontractor.js b/apps/ops/src/api/subcontractor.js new file mode 100644 index 0000000..3187a38 --- /dev/null +++ b/apps/ops/src/api/subcontractor.js @@ -0,0 +1,31 @@ +/** + * Sous-traitants payés à l'acte — appelle targo-hub /acte/*. + * Barème (prix par acte), génération/envoi du lien à token, revue des soumissions. + * Voir services/targo-hub/lib/subcontractor.js. Token hub injecté par nginx /hub (même patron que roster.js). + */ +import { HUB_URL as HUB } from 'src/config/hub' + +async function jget (p) { const r = await fetch(HUB + p); if (!r.ok) throw new Error('Acte API ' + r.status); return r.json() } +async function jpost (p, b) { + const r = await fetch(HUB + p, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(b || {}) }) + if (!r.ok) throw new Error('Acte API ' + r.status); return r.json() +} + +export const getRates = (sub) => jget('/acte/rates' + (sub ? '?sub=' + encodeURIComponent(sub) : '')) // sub → barème effectif du sous-traitant (global + surcharge) +export const saveRates = (rates, sub) => jpost('/acte/rates', sub ? { rates, sub } : { rates }) // sub → surcharge par sous-traitant ; sinon barème global +export const listSubmissions = (ticket) => jget('/acte/list' + (ticket ? '?ticket=' + encodeURIComponent(ticket) : '')) +export const sendLink = (ticket, subcontractor, phone) => jpost('/acte/send', { ticket, subcontractor, phone }) +export const approve = (id, action, reason) => jpost('/acte/approve', { id, action, reason }) // action: 'approve' | 'reject' (gaté admin/superviseur côté hub) +export const imgUrl = (file) => HUB + '/acte/img?file=' + encodeURIComponent(file) // servi gaté via le proxy /hub + +// ── Autofacturation : config fiscale par sous-traitant, relevé par période, marquer payé, export Acomba (CSV) ── +export const getSubs = () => jget('/acte/subs') // { subs:[{name,registrant,gst_no,qst_no,email,phone}], names:[...vus en soumission] } +export const saveSub = (sub) => jpost('/acte/subs', sub) // {name, registrant, gst_no, qst_no, email, phone} — gaté admin/superviseur +const qstr = (sub, from, to) => '?subcontractor=' + encodeURIComponent(sub) + (from ? '&from=' + from : '') + (to ? '&to=' + to : '') +export const getReleve = (sub, from, to) => jget('/acte/releve' + qstr(sub, from, to)) // { releve:{config,lines,subtotal,tps,tvq,total,...} } +export const markPaid = (body) => jpost('/acte/mark-paid', body) // { subcontractor, from, to } OU { ids:[] } — gaté admin/superviseur +export const exportUrl = (sub, from, to) => HUB + '/acte/export' + qstr(sub, from, to) // CSV (téléchargé via le proxy /hub) + +// ── Entente de services : lien web ou PDF des conditions (montré au sous-traitant à la certification) ── +export const getEntente = () => jget('/acte/entente') // { url } +export const saveEntente = (body) => jpost('/acte/entente', body) // { url } | { pdf: dataUrl } | { clear: true } — gaté admin/superviseur diff --git a/apps/ops/src/boot/net-timeout.js b/apps/ops/src/boot/net-timeout.js new file mode 100644 index 0000000..ebf38ff --- /dev/null +++ b/apps/ops/src/boot/net-timeout.js @@ -0,0 +1,26 @@ +// Empêche TOUTE requête de l'app (same-origin : /api, /hub, /auth) de PENDRE indéfiniment. +// Cause du gel « même un clic de menu ne répond plus » : sur session Authentik morte/lente, un fetch +// sans timeout ne résout jamais → la page attendue ne s'affiche pas, la vue ne change pas au clic, +// et les 69 appels /hub (fetch nu) restent suspendus. On borne chaque requête app à 30 s. +// Externes (Mapbox api.mapbox.com, etc.) et appels fournissant déjà un signal : inchangés. +// EventSource (SSE) n'utilise pas window.fetch → non affecté. +import { boot } from 'quasar/wrappers' + +const APP_TIMEOUT_MS = 30000 + +export default boot(() => { + if (typeof window === 'undefined' || window.__netTimeoutInstalled) return + window.__netTimeoutInstalled = true + const orig = window.fetch.bind(window) + const origin = window.location.origin + window.fetch = function (input, init) { + init = init || {} + let url = '' + try { url = typeof input === 'string' ? input : (input && input.url) || '' } catch { /* Request exotique */ } + const sameApp = url.startsWith('/') || url.startsWith(origin) // requêtes app ; exclut absolu cross-origin + if (!sameApp || init.signal) return orig(input, init) // externe ou signal déjà fourni → ne pas toucher + const ctrl = new AbortController() + const id = setTimeout(() => ctrl.abort(), APP_TIMEOUT_MS) + return orig(input, { ...init, signal: ctrl.signal }).finally(() => clearTimeout(id)) + } +}) diff --git a/apps/ops/src/components/customer/ChatterPanel.vue b/apps/ops/src/components/customer/ChatterPanel.vue deleted file mode 100644 index 7dd5877..0000000 --- a/apps/ops/src/components/customer/ChatterPanel.vue +++ /dev/null @@ -1,544 +0,0 @@ - - - - - diff --git a/apps/ops/src/components/customer/ComposeBar.vue b/apps/ops/src/components/customer/ComposeBar.vue deleted file mode 100644 index adf16f5..0000000 --- a/apps/ops/src/components/customer/ComposeBar.vue +++ /dev/null @@ -1,172 +0,0 @@ - - - diff --git a/apps/ops/src/components/customer/ContactCard.vue b/apps/ops/src/components/customer/ContactCard.vue index ee77ca1..8bbd9d7 100644 --- a/apps/ops/src/components/customer/ContactCard.vue +++ b/apps/ops/src/components/customer/ContactCard.vue @@ -22,14 +22,14 @@
- + Envoyer notification {{ lastSentLabel }}
{{ notifyMessage.length }} car.
diff --git a/apps/ops/src/components/customer/CustomerHeader.vue b/apps/ops/src/components/customer/CustomerHeader.vue index 4579430..98fc959 100644 --- a/apps/ops/src/components/customer/CustomerHeader.vue +++ b/apps/ops/src/components/customer/CustomerHeader.vue @@ -45,6 +45,9 @@ + + +