From d901ecebe5967dcf9e998edc0434f8140f815be6 Mon Sep 17 00:00:00 2001 From: Paul Hutchinson Date: Wed, 28 May 2025 11:23:22 +0000 Subject: [PATCH 1/4] chore(sc-65330): Improve error reporting for apps by reporting to Sentry --- .github/workflows/subworkflow-build.yml | 22 +- .gitignore | 3 + package.json | 3 +- pnpm-lock.yaml | 386 +++++++++++++++++++++++- src/instrument.ts | 13 + src/main.tsx | 2 + vite.config.ts | 16 +- 7 files changed, 423 insertions(+), 22 deletions(-) create mode 100644 src/instrument.ts diff --git a/.github/workflows/subworkflow-build.yml b/.github/workflows/subworkflow-build.yml index e8311e7..f49168c 100644 --- a/.github/workflows/subworkflow-build.yml +++ b/.github/workflows/subworkflow-build.yml @@ -75,8 +75,11 @@ jobs: LABELS: "${{ steps.extract_labels.outputs.labels }}" VERSION: "${{ steps.version_tag.outputs.version }}" PUSH_TAG: "${{ inputs.push-tag }}" - RUN_LINT: "${{ inputs.run-lint }}" - RUN_TESTS: "${{ inputs.run-tests }}" + RUN_LINT: "${{ inputs.run-lint }}" + RUN_TESTS: "${{ inputs.run-tests }}" + SENTRY_AUTH_TOKEN: "${{ secrets.SENTRY_AUTH_TOKEN }}" + SENTRY_ORG: "${{ secrets.SENTRY_ORG }}" + SENTRY_PROJECT: "${{ secrets.SENTRY_PROJECT }}" with: env: | LABELS @@ -84,6 +87,9 @@ jobs: PUSH_TAG RUN_LINT RUN_TESTS + SENTRY_AUTH_TOKEN + SENTRY_ORG + SENTRY_PROJECT runCmd: | set -e @@ -98,14 +104,20 @@ jobs: pnpm test:coverage fi - # Build - pnpm run build - # Tag MILESTONE=$(echo "$LABELS" | grep -E 'major-version|minor-version' | head -1) VERSION_NEW=$(pnpm run bumpManifestVer "$MILESTONE" "$VERSION" | tail -n 1) pnpm prettier --write manifest.json git tag -a $VERSION_NEW -m "Version $VERSION_NEW" + + # Build + if [ "$PUSH_TAG" != "true" ]; then + ## Disable Sentry Release if not pushing + SENTRY_DISABLED="true" + fi + pnpm run build + + # Push if [ "$PUSH_TAG" = "true" ]; then git push origin $VERSION_NEW fi diff --git a/.gitignore b/.gitignore index a116337..bad6477 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ dist-ssr *.local /coverage .pnpm-store + +# Sentry Config File +.env.sentry-build-plugin diff --git a/package.json b/package.json index f6e9ef9..9ef6219 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", "@heroicons/react": "1.0.6", + "@sentry/react": "^9.22.0", + "@sentry/vite-plugin": "^3.5.0", "date-fns": "^2.30.0", "flatpickr": "^4.6.13", "formik": "^2.4.6", @@ -30,7 +32,6 @@ "react": "^17.0.2", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^17.0.2", - "react-error-boundary": "^3.1.4", "react-resize-observer": "^1.1.1", "react-router-dom": "^6.30.0", "react-time-ago": "^7.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c28c52b..dd6f34a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,12 @@ importers: '@heroicons/react': specifier: 1.0.6 version: 1.0.6(react@17.0.2) + '@sentry/react': + specifier: ^9.22.0 + version: 9.22.0(react@17.0.2) + '@sentry/vite-plugin': + specifier: ^3.5.0 + version: 3.5.0 date-fns: specifier: ^2.30.0 version: 2.30.0 @@ -53,9 +59,6 @@ importers: react-dom: specifier: ^17.0.2 version: 17.0.2(react@17.0.2) - react-error-boundary: - specifier: ^3.1.4 - version: 3.1.4(react@17.0.2) react-resize-observer: specifier: ^1.1.1 version: 1.1.1(react@17.0.2) @@ -943,6 +946,94 @@ packages: cpu: [x64] os: [win32] + '@sentry-internal/browser-utils@9.22.0': + resolution: {integrity: sha512-Ou1tBnVxFAIn8i9gvrWzRotNJQYiu3awNXpsFCw6qFwmiKAVPa6b13vCdolhXnrIiuR77jY1LQnKh9hXpoRzsg==} + engines: {node: '>=18'} + + '@sentry-internal/feedback@9.22.0': + resolution: {integrity: sha512-zgMVkoC61fgi41zLcSZA59vOtKxcLrKBo1ECYhPD1hxEaneNqY5fhXDwlQBw96P5l2yqkgfX6YZtSdU4ejI9yA==} + engines: {node: '>=18'} + + '@sentry-internal/replay-canvas@9.22.0': + resolution: {integrity: sha512-EcG9IMSEalFe49kowBTJObWjof/iHteDwpyuAszsFDdQUYATrVUtwpwN7o52vDYWJud4arhjrQnMamIGxa79eQ==} + engines: {node: '>=18'} + + '@sentry-internal/replay@9.22.0': + resolution: {integrity: sha512-9GOycoKbrclcRXfcbNV8svbmAsOS5R4wXBQmKF4pFLkmFA/lJv9kdZSNYkRvkrxdNfbMIJXP+DV9EqTZcryXig==} + engines: {node: '>=18'} + + '@sentry/babel-plugin-component-annotate@3.5.0': + resolution: {integrity: sha512-s2go8w03CDHbF9luFGtBHKJp4cSpsQzNVqgIa9Pfa4wnjipvrK6CxVT4icpLA3YO6kg5u622Yoa5GF3cJdippw==} + engines: {node: '>= 14'} + + '@sentry/browser@9.22.0': + resolution: {integrity: sha512-3TeRm74dvX0JdjX0AgkQa+22iUHwHnY+Q6M05NZ+tDeCNHGK/mEBTeqquS1oQX67jWyuvYmG3VV6RJUxtG9Paw==} + engines: {node: '>=18'} + + '@sentry/bundler-plugin-core@3.5.0': + resolution: {integrity: sha512-zDzPrhJqAAy2VzV4g540qAZH4qxzisstK2+NIJPZUUKztWRWUV2cMHsyUtdctYgloGkLyGpZJBE3RE6dmP/xqQ==} + engines: {node: '>= 14'} + + '@sentry/cli-darwin@2.42.2': + resolution: {integrity: sha512-GtJSuxER7Vrp1IpxdUyRZzcckzMnb4N5KTW7sbTwUiwqARRo+wxS+gczYrS8tdgtmXs5XYhzhs+t4d52ITHMIg==} + engines: {node: '>=10'} + os: [darwin] + + '@sentry/cli-linux-arm64@2.42.2': + resolution: {integrity: sha512-BOxzI7sgEU5Dhq3o4SblFXdE9zScpz6EXc5Zwr1UDZvzgXZGosUtKVc7d1LmkrHP8Q2o18HcDWtF3WvJRb5Zpw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux, freebsd] + + '@sentry/cli-linux-arm@2.42.2': + resolution: {integrity: sha512-7udCw+YL9lwq+9eL3WLspvnuG+k5Icg92YE7zsteTzWLwgPVzaxeZD2f8hwhsu+wmL+jNqbpCRmktPteh3i2mg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux, freebsd] + + '@sentry/cli-linux-i686@2.42.2': + resolution: {integrity: sha512-Sw/dQp5ZPvKnq3/y7wIJyxTUJYPGoTX/YeMbDs8BzDlu9to2LWV3K3r7hE7W1Lpbaw4tSquUHiQjP5QHCOS7aQ==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [linux, freebsd] + + '@sentry/cli-linux-x64@2.42.2': + resolution: {integrity: sha512-mU4zUspAal6TIwlNLBV5oq6yYqiENnCWSxtSQVzWs0Jyq97wtqGNG9U+QrnwjJZ+ta/hvye9fvL2X25D/RxHQw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux, freebsd] + + '@sentry/cli-win32-i686@2.42.2': + resolution: {integrity: sha512-iHvFHPGqgJMNqXJoQpqttfsv2GI3cGodeTq4aoVLU/BT3+hXzbV0x1VpvvEhncJkDgDicJpFLM8sEPHb3b8abw==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [win32] + + '@sentry/cli-win32-x64@2.42.2': + resolution: {integrity: sha512-vPPGHjYoaGmfrU7xhfFxG7qlTBacroz5NdT+0FmDn6692D8IvpNXl1K+eV3Kag44ipJBBeR8g1HRJyx/F/9ACw==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@sentry/cli@2.42.2': + resolution: {integrity: sha512-spb7S/RUumCGyiSTg8DlrCX4bivCNmU/A1hcfkwuciTFGu8l5CDc2I6jJWWZw8/0enDGxuj5XujgXvU5tr4bxg==} + engines: {node: '>= 10'} + hasBin: true + + '@sentry/core@9.22.0': + resolution: {integrity: sha512-ixvtKmPF42Y6ckGUbFlB54OWI75H2gO5UYHojO6eXFpS7xO3ZGgV/QH6wb40mWK+0w5XZ0233FuU9VpsuE6mKA==} + engines: {node: '>=18'} + + '@sentry/react@9.22.0': + resolution: {integrity: sha512-mI43NnioBYdG5TiXqRlhV1feZs9bnrrl+k5HOHBK7VQtymaXO0fkcsRLZTkdSgLRLMJGasZuvVhq2xK+18QyWQ==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.14.0 || 17.x || 18.x || 19.x + + '@sentry/vite-plugin@3.5.0': + resolution: {integrity: sha512-jUnpTdpicG8wefamw7eNo2uO+Q3KCbOAiF76xH4gfNHSW6TN2hBfOtmLu7J+ive4c0Al3+NEHz19bIPR0lkwWg==} + engines: {node: '>= 14'} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -1256,6 +1347,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1326,6 +1421,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1506,6 +1605,10 @@ packages: engines: {node: '>=12'} deprecated: Use your platform's native DOMException instead + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + engines: {node: '>=12'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1699,6 +1802,10 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + flat-cache@3.2.0: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -1792,6 +1899,10 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + glob@9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1936,6 +2047,10 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -2265,6 +2380,10 @@ packages: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} @@ -2287,6 +2406,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -2294,6 +2416,10 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + magic-string@0.30.8: + resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} + engines: {node: '>=12'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -2348,9 +2474,21 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} + minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + modern-normalize@1.1.0: resolution: {integrity: sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==} engines: {node: '>=6'} @@ -2375,6 +2513,15 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -2435,6 +2582,10 @@ packages: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -2465,6 +2616,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -2547,6 +2702,9 @@ packages: property-expr@2.0.6: resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + psl@1.15.0: resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} @@ -2576,12 +2734,6 @@ packages: peerDependencies: react: 17.0.2 - react-error-boundary@3.1.4: - resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} - engines: {node: '>=10', npm: '>=6'} - peerDependencies: - react: '>=16.13.1' - react-fast-compare@2.0.4: resolution: {integrity: sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==} @@ -2656,6 +2808,10 @@ packages: resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} engines: {node: '>=0.10.0'} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -2934,6 +3090,9 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@3.0.0: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} @@ -3024,6 +3183,9 @@ packages: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} + unplugin@1.0.1: + resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -3114,10 +3276,20 @@ packages: warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + webpack-sources@3.3.0: + resolution: {integrity: sha512-77R0RDmJfj9dyv5p3bM5pOHa+X8/ZkO9c7kpDstigkC4nIDobadsfSGCwB4bKhMVxqAok8tajaoR8rirM7+VFQ==} + engines: {node: '>=10.13.0'} + + webpack-virtual-modules@0.5.0: + resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} + whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -3130,6 +3302,9 @@ packages: resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} engines: {node: '>=12'} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -4063,6 +4238,105 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.0': optional: true + '@sentry-internal/browser-utils@9.22.0': + dependencies: + '@sentry/core': 9.22.0 + + '@sentry-internal/feedback@9.22.0': + dependencies: + '@sentry/core': 9.22.0 + + '@sentry-internal/replay-canvas@9.22.0': + dependencies: + '@sentry-internal/replay': 9.22.0 + '@sentry/core': 9.22.0 + + '@sentry-internal/replay@9.22.0': + dependencies: + '@sentry-internal/browser-utils': 9.22.0 + '@sentry/core': 9.22.0 + + '@sentry/babel-plugin-component-annotate@3.5.0': {} + + '@sentry/browser@9.22.0': + dependencies: + '@sentry-internal/browser-utils': 9.22.0 + '@sentry-internal/feedback': 9.22.0 + '@sentry-internal/replay': 9.22.0 + '@sentry-internal/replay-canvas': 9.22.0 + '@sentry/core': 9.22.0 + + '@sentry/bundler-plugin-core@3.5.0': + dependencies: + '@babel/core': 7.26.10 + '@sentry/babel-plugin-component-annotate': 3.5.0 + '@sentry/cli': 2.42.2 + dotenv: 16.5.0 + find-up: 5.0.0 + glob: 9.3.5 + magic-string: 0.30.8 + unplugin: 1.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@sentry/cli-darwin@2.42.2': + optional: true + + '@sentry/cli-linux-arm64@2.42.2': + optional: true + + '@sentry/cli-linux-arm@2.42.2': + optional: true + + '@sentry/cli-linux-i686@2.42.2': + optional: true + + '@sentry/cli-linux-x64@2.42.2': + optional: true + + '@sentry/cli-win32-i686@2.42.2': + optional: true + + '@sentry/cli-win32-x64@2.42.2': + optional: true + + '@sentry/cli@2.42.2': + dependencies: + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + progress: 2.0.3 + proxy-from-env: 1.1.0 + which: 2.0.2 + optionalDependencies: + '@sentry/cli-darwin': 2.42.2 + '@sentry/cli-linux-arm': 2.42.2 + '@sentry/cli-linux-arm64': 2.42.2 + '@sentry/cli-linux-i686': 2.42.2 + '@sentry/cli-linux-x64': 2.42.2 + '@sentry/cli-win32-i686': 2.42.2 + '@sentry/cli-win32-x64': 2.42.2 + transitivePeerDependencies: + - encoding + - supports-color + + '@sentry/core@9.22.0': {} + + '@sentry/react@9.22.0(react@17.0.2)': + dependencies: + '@sentry/browser': 9.22.0 + '@sentry/core': 9.22.0 + hoist-non-react-statics: 3.3.2 + react: 17.0.2 + + '@sentry/vite-plugin@3.5.0': + dependencies: + '@sentry/bundler-plugin-core': 3.5.0 + unplugin: 1.0.1 + transitivePeerDependencies: + - encoding + - supports-color + '@sinclair/typebox@0.27.8': {} '@sinonjs/commons@3.0.1': @@ -4451,6 +4725,8 @@ snapshots: balanced-match@1.0.2: {} + binary-extensions@2.3.0: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -4522,6 +4798,18 @@ snapshots: char-regex@1.0.2: {} + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -4693,6 +4981,8 @@ snapshots: dependencies: webidl-conversions: 7.0.0 + dotenv@16.5.0: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4948,6 +5238,11 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + flat-cache@3.2.0: dependencies: flatted: 3.3.3 @@ -5060,6 +5355,13 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + glob@9.3.5: + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.11.1 + globals@11.12.0: {} globals@13.24.0: @@ -5213,6 +5515,10 @@ snapshots: dependencies: has-bigints: 1.1.0 + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -5745,6 +6051,10 @@ snapshots: dependencies: p-locate: 4.1.0 + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + lodash-es@4.17.21: {} lodash.debounce@4.0.8: {} @@ -5761,12 +6071,18 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@10.4.3: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 lz-string@1.5.0: {} + magic-string@0.30.8: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + make-dir@4.0.0: dependencies: semver: 7.7.1 @@ -5810,8 +6126,16 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@8.0.4: + dependencies: + brace-expansion: 2.0.1 + minimist@1.2.8: {} + minipass@4.2.8: {} + + minipass@7.1.2: {} + modern-normalize@1.1.0: {} ms@2.1.3: {} @@ -5827,6 +6151,10 @@ snapshots: node-addon-api@7.1.1: optional: true + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-int64@0.4.0: {} node-releases@2.0.19: {} @@ -5888,6 +6216,10 @@ snapshots: dependencies: p-limit: 2.3.0 + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + p-try@2.2.0: {} parent-module@1.0.1: @@ -5913,6 +6245,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + path-type@4.0.0: {} penpal@6.2.1: {} @@ -5989,6 +6326,8 @@ snapshots: property-expr@2.0.6: {} + proxy-from-env@1.1.0: {} + psl@1.15.0: dependencies: punycode: 2.3.1 @@ -6018,11 +6357,6 @@ snapshots: react: 17.0.2 scheduler: 0.20.2 - react-error-boundary@3.1.4(react@17.0.2): - dependencies: - '@babel/runtime': 7.27.0 - react: 17.0.2 - react-fast-compare@2.0.4: {} react-fast-compare@3.2.2: {} @@ -6102,6 +6436,10 @@ snapshots: loose-envify: 1.4.0 object-assign: 4.1.1 + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + readdirp@4.1.2: {} redent@3.0.0: @@ -6416,6 +6754,8 @@ snapshots: universalify: 0.2.0 url-parse: 1.5.10 + tr46@0.0.3: {} + tr46@3.0.0: dependencies: punycode: 2.3.1 @@ -6478,6 +6818,13 @@ snapshots: universalify@0.2.0: {} + unplugin@1.0.1: + dependencies: + acorn: 8.14.1 + chokidar: 3.6.0 + webpack-sources: 3.3.0 + webpack-virtual-modules: 0.5.0 + update-browserslist-db@1.1.3(browserslist@4.24.4): dependencies: browserslist: 4.24.4 @@ -6537,8 +6884,14 @@ snapshots: dependencies: loose-envify: 1.4.0 + webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} + webpack-sources@3.3.0: {} + + webpack-virtual-modules@0.5.0: {} + whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 @@ -6550,6 +6903,11 @@ snapshots: tr46: 3.0.0 webidl-conversions: 7.0.0 + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/src/instrument.ts b/src/instrument.ts new file mode 100644 index 0000000..91c9647 --- /dev/null +++ b/src/instrument.ts @@ -0,0 +1,13 @@ +import * as Sentry from "@sentry/react"; + +const urlParams = new URLSearchParams(document.location.search); +const sentryDsn = urlParams.get('_sentry_dsn'); +const sentryTunnel = urlParams.get('_sentry_tunnel') || undefined; + +if (sentryDsn) { + Sentry.init({ + dsn: sentryDsn, + sendDefaultPii: false, + tunnel: sentryTunnel + }); +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index f6f6d6c..cace4fd 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,3 +1,5 @@ +import * as Sentry from '@sentry/react'; +import './instrument'; import React from "react"; import ReactDOM from "react-dom"; import { HashRouter } from "react-router-dom"; diff --git a/vite.config.ts b/vite.config.ts index a26858b..bcaa4ad 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,3 +1,4 @@ +import { sentryVitePlugin } from "@sentry/vite-plugin"; import path from "path"; import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; @@ -10,13 +11,22 @@ const PORT = process.env.VITE_DEV_SERVER_PORT // https://vitejs.dev/config/ export default defineConfig({ base: "", - plugins: [react()], + plugins: [ + react(), + ...( + process.env.SENTRY_DISABLED !== "true" && process.env.SENTRY_ORG && process.env.SENTRY_PROJECT + ? [sentryVitePlugin({ + org: process.env.SENTRY_ORG, + project: process.env.SENTRY_PROJECT, + })] : [] + ), + ], server: { host: true, port: PORT, allowedHosts: true }, - resolve:{ + resolve: { alias: { "@": path.resolve(__dirname, "src"), }, @@ -42,5 +52,7 @@ export default defineConfig({ }), ], }, + + sourcemap: true }, }); \ No newline at end of file From 7709517a8fdf5cdbe2a346b02d2a2619f78a6524 Mon Sep 17 00:00:00 2001 From: Paul Hutchinson Date: Wed, 28 May 2025 11:41:54 +0000 Subject: [PATCH 2/4] chore(sc-65330): Improve error reporting for apps by reporting to Sentry --- .github/workflows/subworkflow-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/subworkflow-build.yml b/.github/workflows/subworkflow-build.yml index f49168c..5560bcb 100644 --- a/.github/workflows/subworkflow-build.yml +++ b/.github/workflows/subworkflow-build.yml @@ -113,7 +113,7 @@ jobs: # Build if [ "$PUSH_TAG" != "true" ]; then ## Disable Sentry Release if not pushing - SENTRY_DISABLED="true" + export SENTRY_DISABLED="true" fi pnpm run build From cf2bd28302ffc8cf0ae89193db2f2819e79b03bb Mon Sep 17 00:00:00 2001 From: Paul Hutchinson Date: Wed, 28 May 2025 13:43:46 +0000 Subject: [PATCH 3/4] chore(sc-65330): Improve error reporting for apps by reporting to Sentry --- .github/workflows/subworkflow-build.yml | 1 + vite.config.ts | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/subworkflow-build.yml b/.github/workflows/subworkflow-build.yml index 5560bcb..183d057 100644 --- a/.github/workflows/subworkflow-build.yml +++ b/.github/workflows/subworkflow-build.yml @@ -126,6 +126,7 @@ jobs: working-directory: dist run: | cp ../manifest.json . + rm **/*.js.map || echo "No source maps to remove" zip -rq ../app.zip * - name: Upload package diff --git a/vite.config.ts b/vite.config.ts index bcaa4ad..e9e85ab 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -32,6 +32,7 @@ export default defineConfig({ }, }, build: { + sourcemap: true, rollupOptions: { onwarn(warning, warn) { if (warning.code === "MODULE_LEVEL_DIRECTIVE") { @@ -52,7 +53,5 @@ export default defineConfig({ }), ], }, - - sourcemap: true }, }); \ No newline at end of file From 21c7417e7ef1c54fa1549c460382253f7f89e824 Mon Sep 17 00:00:00 2001 From: Paul Hutchinson Date: Wed, 28 May 2025 15:15:35 +0000 Subject: [PATCH 4/4] chore(sc-65330): Improve error reporting for apps by reporting to Sentry --- src/App.tsx | 418 +++++++++++++++++++++++++-------------------------- src/main.tsx | 23 ++- 2 files changed, 220 insertions(+), 221 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a24de47..68fe777 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,29 +1,28 @@ import { useEffect, Suspense } from "react"; import { useDebouncedCallback } from "use-debounce"; import { Routes, Route, useNavigate, useLocation } from "react-router-dom"; -import { ErrorBoundary } from "react-error-boundary"; import { match } from "ts-pattern"; import { - Context, - TargetAction, - LoadingSpinner, - useDeskproElements, - useDeskproAppClient, - useDeskproAppEvents, - useInitialisedDeskproAppClient, + Context, + TargetAction, + LoadingSpinner, + useDeskproElements, + useDeskproAppClient, + useDeskproAppEvents, + useInitialisedDeskproAppClient, } from "@deskpro/app-sdk"; import { - AppElementPayload, - ReplyBoxSelection, + AppElementPayload, + ReplyBoxSelection, } from "./context/StoreProvider/types"; import { useStore } from "./context/StoreProvider/hooks"; import { baseRequest, getIssueCommentUrl } from "./services/github"; import { useLogout, useUnlinkIssue } from "./hooks"; import { - ticketReplyNotesSelectionStateKey, - ticketReplyEmailsSelectionStateKey, - registerReplyBoxNotesAdditionsTargetAction, - registerReplyBoxEmailsAdditionsTargetAction, + ticketReplyNotesSelectionStateKey, + ticketReplyEmailsSelectionStateKey, + registerReplyBoxNotesAdditionsTargetAction, + registerReplyBoxEmailsAdditionsTargetAction, } from "./utils/replyBox"; import { LogInPage } from "./pages/LogIn"; import { HomePage } from "./pages/HomePage"; @@ -35,207 +34,208 @@ import { AdminPage } from "./pages/AdminPage"; import { Main } from "./pages/Main"; import { ErrorBlock, Loading, AppContainer } from "./components/common"; import { IssueGQL } from "./services/github/types"; +import { ErrorBoundary } from "@sentry/react"; const App = () => { - const navigate = useNavigate(); - const { pathname } = useLocation(); - const [state, dispatch] = useStore(); - const { client } = useDeskproAppClient(); - const { logout, isLoading: isLoadingLogout } = useLogout(); - const { unlinkIssue, isLoading: isLoadingUnlink } = useUnlinkIssue(); - const isAdmin = pathname.includes("/admin/"); - - const isLoading = [ - isLoadingUnlink, - isLoadingLogout, - ].some((isLoading) => isLoading); - - if (state._error) { - // eslint-disable-next-line no-console - console.error(`GitHub: ${state._error}`); + const navigate = useNavigate(); + const { pathname } = useLocation(); + const [state, dispatch] = useStore(); + const { client } = useDeskproAppClient(); + const { logout, isLoading: isLoadingLogout } = useLogout(); + const { unlinkIssue, isLoading: isLoadingUnlink } = useUnlinkIssue(); + const isAdmin = pathname.includes("/admin/"); + + const isLoading = [ + isLoadingUnlink, + isLoadingLogout, + ].some((isLoading) => isLoading); + + if (state._error) { + // eslint-disable-next-line no-console + console.error(`GitHub: ${state._error}`); + } + + useEffect(() => { + if (state.isAuth) { + navigate("/home"); } - - useEffect(() => { - if (state.isAuth) { - navigate("/home"); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.isAuth]); - - useDeskproElements(({ registerElement }) => { - registerElement("refresh", { type: "refresh_button" }); - }); - - useInitialisedDeskproAppClient((client) => { - registerReplyBoxNotesAdditionsTargetAction(client, state); - registerReplyBoxEmailsAdditionsTargetAction(client, state); - client.registerTargetAction("githubOnReplyBoxNote", "on_reply_box_note"); - client.registerTargetAction("githubOnReplyBoxEmail", "on_reply_box_email"); - }, [state.issues, state.context?.data]); - - const debounceTargetAction = useDebouncedCallback<(a: TargetAction) => void>( - (action: TargetAction) => { - dispatch({ type: "error", error: null }); - - match(action.name) - .with("linkTicket", () => navigate("/link_issue")) - .with("githubOnReplyBoxNote", () => { - const ticketId = action.subject; - const note = action.payload.note; - - if (!ticketId || !note || !client) { - return; - } - - if (ticketId !== state.context?.data.ticket.id) { - return; - } - - client.setBlocking(true); - client.getState(`tickets/${ticketId}/github/notes/*`) - .then((r) => { - const commentUrls = r - .filter(({ data }) => data?.selected) - .map(({ data }) => data?.id) - .map((issueId) => (state.issues ?? []).find(({ id }) => issueId === id.toLowerCase()) as IssueGQL) - .filter((issue) => !!issue) - .map(({ repository, number }) => getIssueCommentUrl(repository.nameWithOwner, number) ); - - return Promise.all(commentUrls.map((commentUrl) => baseRequest(client, { - rawUrl: commentUrl, - method: "POST", - data: { - body: note, - }, - }))); - }) - .then(() => dispatch({ type: "setIssue", issue: null })) - .finally(() => client.setBlocking(false)) - }) - .with("githubOnReplyBoxEmail", () => { - const ticketId = action.subject; - const email = action.payload.email; - - if (!ticketId || !email || !client) { - return; - } - - if (ticketId !== state.context?.data.ticket.id) { - return; - } - - client.setBlocking(true); - client.getState(`tickets/${ticketId}/github/emails/*`) - .then((r) => { - const commentUrls = r - .filter(({ data }) => data?.selected) - .map(({ data }) => data?.id) - .map((issueId) => (state.issues ?? []).find(({ id }) => issueId === id.toLowerCase()) as IssueGQL) - .filter((issue) => !!issue) - .map(({ repository, number }) => getIssueCommentUrl(repository.nameWithOwner, number) ); - - return Promise.all(commentUrls.map((commentUrl) => baseRequest(client, { - rawUrl: commentUrl, - method: "POST", - data: { - body: email - } - }))); - }) - .then(() => dispatch({ type: "setIssue", issue: null })) - .finally(() => client.setBlocking(false)) - }) - .with("githubReplyBoxNoteAdditions", () => { - (action.payload ?? []).forEach((selection: { id: string; selected: boolean; }) => { - const ticketId = action.subject; - - if (state.context?.data.ticket.id) { - client?.setState( - ticketReplyNotesSelectionStateKey(ticketId, selection.id), - { id: selection.id, selected: selection.selected } - ).then((result) => { - if (result.isSuccess) { - registerReplyBoxNotesAdditionsTargetAction(client, state); - } else if (!result.isSuccess && result.errors.length) { - dispatch({ type: "error", error: result.errors }); - } - }); - } - }) - }) - .with("githubReplyBoxEmailAdditions", () => { - (action.payload ?? []).forEach((selection: { id: string; selected: boolean; }) => { - const ticketId = action.subject; - - if (state.context?.data.ticket.id) { - client?.setState( - ticketReplyEmailsSelectionStateKey(ticketId, selection.id), - { id: selection.id, selected: selection.selected } - ).then((result) => { - if (result.isSuccess) { - registerReplyBoxEmailsAdditionsTargetAction(client, state); - } else if (!result.isSuccess && result.errors.length) { - dispatch({ type: "error", error: result.errors }); - } - }); - } - }) - }) - .run(); - }, - 500, - ); - - useDeskproAppEvents({ - onShow: () => { - client && setTimeout(() => client.resize(), 200); - }, - onChange: (context: Context) => { - context && dispatch({ type: "loadContext", context }); - }, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - onElementEvent: (id: string, type: string, payload?: AppElementPayload) => { - if (payload?.type === "changePage") { - navigate(payload.params); - } else if (payload?.type === "logout") { - logout(); - } else if (payload?.type === "unlinkTicket") { - unlinkIssue(payload); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [state.isAuth]); + + useDeskproElements(({ registerElement }) => { + registerElement("refresh", { type: "refresh_button" }); + }); + + useInitialisedDeskproAppClient((client) => { + registerReplyBoxNotesAdditionsTargetAction(client, state); + registerReplyBoxEmailsAdditionsTargetAction(client, state); + client.registerTargetAction("githubOnReplyBoxNote", "on_reply_box_note"); + client.registerTargetAction("githubOnReplyBoxEmail", "on_reply_box_email"); + }, [state.issues, state.context?.data]); + + const debounceTargetAction = useDebouncedCallback<(a: TargetAction) => void>( + (action: TargetAction) => { + dispatch({ type: "error", error: null }); + + match(action.name) + .with("linkTicket", () => navigate("/link_issue")) + .with("githubOnReplyBoxNote", () => { + const ticketId = action.subject; + const note = action.payload.note; + + if (!ticketId || !note || !client) { + return; + } + + if (ticketId !== state.context?.data.ticket.id) { + return; + } + + client.setBlocking(true); + client.getState(`tickets/${ticketId}/github/notes/*`) + .then((r) => { + const commentUrls = r + .filter(({ data }) => data?.selected) + .map(({ data }) => data?.id) + .map((issueId) => (state.issues ?? []).find(({ id }) => issueId === id.toLowerCase()) as IssueGQL) + .filter((issue) => !!issue) + .map(({ repository, number }) => getIssueCommentUrl(repository.nameWithOwner, number) ); + + return Promise.all(commentUrls.map((commentUrl) => baseRequest(client, { + rawUrl: commentUrl, + method: "POST", + data: { + body: note, + }, + }))); + }) + .then(() => dispatch({ type: "setIssue", issue: null })) + .finally(() => client.setBlocking(false)) + }) + .with("githubOnReplyBoxEmail", () => { + const ticketId = action.subject; + const email = action.payload.email; + + if (!ticketId || !email || !client) { + return; + } + + if (ticketId !== state.context?.data.ticket.id) { + return; + } + + client.setBlocking(true); + client.getState(`tickets/${ticketId}/github/emails/*`) + .then((r) => { + const commentUrls = r + .filter(({ data }) => data?.selected) + .map(({ data }) => data?.id) + .map((issueId) => (state.issues ?? []).find(({ id }) => issueId === id.toLowerCase()) as IssueGQL) + .filter((issue) => !!issue) + .map(({ repository, number }) => getIssueCommentUrl(repository.nameWithOwner, number) ); + + return Promise.all(commentUrls.map((commentUrl) => baseRequest(client, { + rawUrl: commentUrl, + method: "POST", + data: { + body: email + } + }))); + }) + .then(() => dispatch({ type: "setIssue", issue: null })) + .finally(() => client.setBlocking(false)) + }) + .with("githubReplyBoxNoteAdditions", () => { + (action.payload ?? []).forEach((selection: { id: string; selected: boolean; }) => { + const ticketId = action.subject; + + if (state.context?.data.ticket.id) { + client?.setState( + ticketReplyNotesSelectionStateKey(ticketId, selection.id), + { id: selection.id, selected: selection.selected } + ).then((result) => { + if (result.isSuccess) { + registerReplyBoxNotesAdditionsTargetAction(client, state); + } else if (!result.isSuccess && result.errors.length) { + dispatch({ type: "error", error: result.errors }); + } + }); } - - match(type) - .with("home_button", () => dispatch({ type: "setIssue", issue: null })) - .otherwise(() => {}); - }, - onTargetAction: (a) => debounceTargetAction(a as TargetAction), - }, [client, unlinkIssue, logout]); - - if (isLoading) { - return ( - - ); - } - + }) + }) + .with("githubReplyBoxEmailAdditions", () => { + (action.payload ?? []).forEach((selection: { id: string; selected: boolean; }) => { + const ticketId = action.subject; + + if (state.context?.data.ticket.id) { + client?.setState( + ticketReplyEmailsSelectionStateKey(ticketId, selection.id), + { id: selection.id, selected: selection.selected } + ).then((result) => { + if (result.isSuccess) { + registerReplyBoxEmailsAdditionsTargetAction(client, state); + } else if (!result.isSuccess && result.errors.length) { + dispatch({ type: "error", error: result.errors }); + } + }); + } + }) + }) + .run(); + }, + 500, + ); + + useDeskproAppEvents({ + onShow: () => { + client && setTimeout(() => client.resize(), 200); + }, + onChange: (context: Context) => { + context && dispatch({ type: "loadContext", context }); + }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + onElementEvent: (id: string, type: string, payload?: AppElementPayload) => { + if (payload?.type === "changePage") { + navigate(payload.params); + } else if (payload?.type === "logout") { + logout(); + } else if (payload?.type === "unlinkTicket") { + unlinkIssue(payload); + } + + match(type) + .with("home_button", () => dispatch({ type: "setIssue", issue: null })) + .otherwise(() => {}); + }, + onTargetAction: (a) => debounceTargetAction(a as TargetAction), + }, [client, unlinkIssue, logout]); + + if (isLoading) { return ( - - {state._error ? : ""} - }> - ()}> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - + ); + } + + return ( + + {state._error ? : ""} + }> + ()}> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + ); } export default App; diff --git a/src/main.tsx b/src/main.tsx index cace4fd..e8d2a36 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,4 +1,3 @@ -import * as Sentry from '@sentry/react'; import './instrument'; import React from "react"; import ReactDOM from "react-dom"; @@ -20,15 +19,15 @@ import "./main.css"; TimeAgo.addDefaultLocale(en); ReactDOM.render(( - - - - - - - - - - - + + + + + + + + + + + ), document.getElementById("root"));