diff --git a/.github/workflows/onesky.yml b/.github/workflows/onesky.yml index fd12e4613..5d4ba0a2b 100644 --- a/.github/workflows/onesky.yml +++ b/.github/workflows/onesky.yml @@ -43,12 +43,14 @@ jobs: sign-commits: true commit-message: Run `yarn extract` and `yarn onesky:download` title: '[no-Jira] Update translations' - body: > - Update translations with the latest labels extracted from the components and downloaded from OneSky.\n\n + body: | + Update translations with the latest labels extracted from the components and downloaded from OneSky. + Due to GitHub Actions restrictions, our `on: pull_request` CI workflow will not run automatically because this PR was created by a bot. - To get it to run, a human will have to push a commit to the branch.\n\n - ```sh\n - $ git switch bot-update-translations\n - $ git commit --allow-empty --message "Bump CI"\n - $ git push\n + To get it to run, a human will have to push a commit to the branch. + + ```sh + $ git switch bot-update-translations + $ git commit --allow-empty --message "Bump CI" + $ git push ``` diff --git a/.pnp.cjs b/.pnp.cjs index febbddb53..832612283 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -78,7 +78,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/testing-library__jest-dom", "npm:5.14.5"],\ ["@types/uuid", "npm:9.0.1"],\ ["@typescript-eslint/eslint-plugin", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ - ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ + ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:8.17.0"],\ ["apollo-datasource-rest", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:3.7.0"],\ ["apollo-server-micro", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:3.11.1"],\ ["apollo3-cache-persist", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:0.14.1"],\ @@ -8212,7 +8212,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ]],\ ["@surma/rollup-plugin-off-main-thread", [\ ["npm:2.2.3", {\ - "packageLocation": "./.yarn/unplugged/@surma-rollup-plugin-off-main-thread-npm-2.2.3-1f57d3eded/node_modules/@surma/rollup-plugin-off-main-thread/",\ + "packageLocation": "./.yarn/cache/@surma-rollup-plugin-off-main-thread-npm-2.2.3-1f57d3eded-2c02134944.zip/node_modules/@surma/rollup-plugin-off-main-thread/",\ "packageDependencies": [\ ["@surma/rollup-plugin-off-main-thread", "npm:2.2.3"],\ ["ejs", "npm:3.1.10"],\ @@ -9025,7 +9025,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/eslint", null],\ ["@types/typescript", null],\ ["@types/typescript-eslint__parser", null],\ - ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ + ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:8.17.0"],\ ["@typescript-eslint/scope-manager", "npm:7.5.0"],\ ["@typescript-eslint/type-utils", "virtual:ffbd544ad0fe91b6a84d26da1b983efebd6dadd9a2ae0160d3effbd386edafd313452293d39a630ddc390a351a5cc7679a4b023d895be27d9a3a0972f47b5e95#npm:7.5.0"],\ ["@typescript-eslint/utils", "virtual:ffbd544ad0fe91b6a84d26da1b983efebd6dadd9a2ae0160d3effbd386edafd313452293d39a630ddc390a351a5cc7679a4b023d895be27d9a3a0972f47b5e95#npm:7.5.0"],\ @@ -9051,23 +9051,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }]\ ]],\ ["@typescript-eslint/parser", [\ - ["npm:7.5.0", {\ - "packageLocation": "./.yarn/cache/@typescript-eslint-parser-npm-7.5.0-950f9b5b79-c9f85ae638.zip/node_modules/@typescript-eslint/parser/",\ + ["npm:8.17.0", {\ + "packageLocation": "./.yarn/cache/@typescript-eslint-parser-npm-8.17.0-f09f5fe974-3d330fc777.zip/node_modules/@typescript-eslint/parser/",\ "packageDependencies": [\ - ["@typescript-eslint/parser", "npm:7.5.0"]\ + ["@typescript-eslint/parser", "npm:8.17.0"]\ ],\ "linkType": "SOFT"\ }],\ - ["virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0", {\ - "packageLocation": "./.yarn/__virtual__/@typescript-eslint-parser-virtual-ff1f3e1176/0/cache/@typescript-eslint-parser-npm-7.5.0-950f9b5b79-c9f85ae638.zip/node_modules/@typescript-eslint/parser/",\ + ["virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:8.17.0", {\ + "packageLocation": "./.yarn/__virtual__/@typescript-eslint-parser-virtual-30f2e23c7f/0/cache/@typescript-eslint-parser-npm-8.17.0-f09f5fe974-3d330fc777.zip/node_modules/@typescript-eslint/parser/",\ "packageDependencies": [\ - ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ + ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:8.17.0"],\ ["@types/eslint", null],\ ["@types/typescript", null],\ - ["@typescript-eslint/scope-manager", "npm:7.5.0"],\ - ["@typescript-eslint/types", "npm:7.5.0"],\ - ["@typescript-eslint/typescript-estree", "virtual:0cb9aadbb04938258c97b7cbc489f605e3990fc27740176fcd5b75b9e0cbc7e0332ee441d29d653ee98091fea9acfceb90c7fe89e3796da0a24d0b189182f300#npm:7.5.0"],\ - ["@typescript-eslint/visitor-keys", "npm:7.5.0"],\ + ["@typescript-eslint/scope-manager", "npm:8.17.0"],\ + ["@typescript-eslint/types", "npm:8.17.0"],\ + ["@typescript-eslint/typescript-estree", "virtual:30f2e23c7ffcf891f87a18cb58d472e2cdd77c8ba41282172fab4ab97aca549224dfc8214dd79bce8fef1f272ed1c733a2ee10d12249d23d48590573865fe339#npm:8.17.0"],\ + ["@typescript-eslint/visitor-keys", "npm:8.17.0"],\ ["debug", "virtual:4758feee42453c0e31b0d2032a7b1362d6b06281699830d2da9a056f2cca72bd2c5cfdb74005fdf03a64876be8eaca2dd7b0fc2dc59d14318badf19cb22ba18e#npm:4.3.4"],\ ["eslint", "npm:8.57.0"],\ ["typescript", "patch:typescript@npm%3A5.6.3#~builtin::version=5.6.3&hash=8c6c40"]\ @@ -9099,6 +9099,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/visitor-keys", "npm:7.5.0"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:8.17.0", {\ + "packageLocation": "./.yarn/cache/@typescript-eslint-scope-manager-npm-8.17.0-f0be17d23c-c5f628e5b4.zip/node_modules/@typescript-eslint/scope-manager/",\ + "packageDependencies": [\ + ["@typescript-eslint/scope-manager", "npm:8.17.0"],\ + ["@typescript-eslint/types", "npm:8.17.0"],\ + ["@typescript-eslint/visitor-keys", "npm:8.17.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@typescript-eslint/type-utils", [\ @@ -9145,6 +9154,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/types", "npm:7.5.0"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:8.17.0", {\ + "packageLocation": "./.yarn/cache/@typescript-eslint-types-npm-8.17.0-ad6f5ae49f-5f6933903c.zip/node_modules/@typescript-eslint/types/",\ + "packageDependencies": [\ + ["@typescript-eslint/types", "npm:8.17.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@typescript-eslint/typescript-estree", [\ @@ -9162,6 +9178,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ],\ "linkType": "SOFT"\ }],\ + ["npm:8.17.0", {\ + "packageLocation": "./.yarn/cache/@typescript-eslint-typescript-estree-npm-8.17.0-0110955b24-35d3dca3cd.zip/node_modules/@typescript-eslint/typescript-estree/",\ + "packageDependencies": [\ + ["@typescript-eslint/typescript-estree", "npm:8.17.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ ["virtual:09b157fecc804d318ed6a4566b4fd8284d6c425c8f1d0e01ffa1f243f725634a1bd90dde6adac603624e360c4421ea2a697f3d3c81cceace25e2f7fdba859625#npm:5.33.1", {\ "packageLocation": "./.yarn/__virtual__/@typescript-eslint-typescript-estree-virtual-1492b0a79a/0/cache/@typescript-eslint-typescript-estree-npm-5.33.1-1b514c2400-1418e409b1.zip/node_modules/@typescript-eslint/typescript-estree/",\ "packageDependencies": [\ @@ -9203,6 +9226,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ],\ "linkType": "HARD"\ }],\ + ["virtual:30f2e23c7ffcf891f87a18cb58d472e2cdd77c8ba41282172fab4ab97aca549224dfc8214dd79bce8fef1f272ed1c733a2ee10d12249d23d48590573865fe339#npm:8.17.0", {\ + "packageLocation": "./.yarn/__virtual__/@typescript-eslint-typescript-estree-virtual-ff45382f28/0/cache/@typescript-eslint-typescript-estree-npm-8.17.0-0110955b24-35d3dca3cd.zip/node_modules/@typescript-eslint/typescript-estree/",\ + "packageDependencies": [\ + ["@typescript-eslint/typescript-estree", "virtual:30f2e23c7ffcf891f87a18cb58d472e2cdd77c8ba41282172fab4ab97aca549224dfc8214dd79bce8fef1f272ed1c733a2ee10d12249d23d48590573865fe339#npm:8.17.0"],\ + ["@types/typescript", null],\ + ["@typescript-eslint/types", "npm:8.17.0"],\ + ["@typescript-eslint/visitor-keys", "npm:8.17.0"],\ + ["debug", "virtual:4758feee42453c0e31b0d2032a7b1362d6b06281699830d2da9a056f2cca72bd2c5cfdb74005fdf03a64876be8eaca2dd7b0fc2dc59d14318badf19cb22ba18e#npm:4.3.4"],\ + ["fast-glob", "npm:3.3.2"],\ + ["is-glob", "npm:4.0.3"],\ + ["minimatch", "npm:9.0.5"],\ + ["semver", "npm:7.6.3"],\ + ["ts-api-utils", "virtual:ff45382f289877ead7011223e1a3e12ad5931bc5bb966ce16d645a7d92cd2edad32394d9fb08d21093dea26aa08aeb03e17de0977d68d796d54585998a88576c#npm:1.4.3"],\ + ["typescript", "patch:typescript@npm%3A5.6.3#~builtin::version=5.6.3&hash=8c6c40"]\ + ],\ + "packagePeers": [\ + "@types/typescript",\ + "typescript"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:83b5808f266aeb1551a5f18e8473d1be0048c9b8fe4d71b11e46c49ef84098857bd6c7c43bb6925e75a393f7b88790afcce73d5fa58c2822236c7e167b9fc140#npm:7.5.0", {\ "packageLocation": "./.yarn/__virtual__/@typescript-eslint-typescript-estree-virtual-0f7794eca8/0/cache/@typescript-eslint-typescript-estree-npm-7.5.0-4235eaf12f-ebc6838af9.zip/node_modules/@typescript-eslint/typescript-estree/",\ "packageDependencies": [\ @@ -9298,6 +9342,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-visitor-keys", "npm:3.4.3"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:8.17.0", {\ + "packageLocation": "./.yarn/cache/@typescript-eslint-visitor-keys-npm-8.17.0-9c7a8b5908-f92f659ec8.zip/node_modules/@typescript-eslint/visitor-keys/",\ + "packageDependencies": [\ + ["@typescript-eslint/visitor-keys", "npm:8.17.0"],\ + ["@typescript-eslint/types", "npm:8.17.0"],\ + ["eslint-visitor-keys", "npm:4.2.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["@ungap/structured-clone", [\ @@ -13954,7 +14007,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/eslint-import-resolver-typescript", null],\ ["@types/eslint-import-resolver-webpack", null],\ ["@types/typescript-eslint__parser", null],\ - ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ + ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:8.17.0"],\ ["debug", "virtual:d9426786c635bc4b52511d6cc4b56156f50d780a698c0e20fc6caf10d3be51cbf176e79cff882f4d42a23ff4d0f89fe94222849578214e7fbae0f2754c82af02#npm:3.2.7"],\ ["eslint", "npm:8.57.0"],\ ["eslint-import-resolver-node", "npm:0.3.6"],\ @@ -13990,7 +14043,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-plugin-import", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:2.26.0"],\ ["@types/eslint", null],\ ["@types/typescript-eslint__parser", null],\ - ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ + ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:8.17.0"],\ ["array-includes", "npm:3.1.5"],\ ["array.prototype.flat", "npm:1.3.0"],\ ["debug", "virtual:4b6d71baf8d43e6e9ab2e034c76a127a59a4f52c68b4d690a67d2337edcbba20c586f42f868bf9e27d2a8c265a2f8ed46b0a406f3d925c97696dc47fd870e967#npm:2.6.9"],\ @@ -14212,6 +14265,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["eslint-visitor-keys", "npm:3.4.3"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:4.2.0", {\ + "packageLocation": "./.yarn/cache/eslint-visitor-keys-npm-4.2.0-a591434af3-779c604672.zip/node_modules/eslint-visitor-keys/",\ + "packageDependencies": [\ + ["eslint-visitor-keys", "npm:4.2.0"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["espree", [\ @@ -14625,6 +14685,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["micromatch", "npm:4.0.8"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:3.3.2", {\ + "packageLocation": "./.yarn/cache/fast-glob-npm-3.3.2-0a8cb4f2ca-900e4979f4.zip/node_modules/fast-glob/",\ + "packageDependencies": [\ + ["fast-glob", "npm:3.3.2"],\ + ["@nodelib/fs.stat", "npm:2.0.5"],\ + ["@nodelib/fs.walk", "npm:1.2.8"],\ + ["glob-parent", "npm:5.1.2"],\ + ["merge2", "npm:1.4.1"],\ + ["micromatch", "npm:4.0.8"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["fast-json-stable-stringify", [\ @@ -19236,6 +19308,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["brace-expansion", "npm:2.0.1"]\ ],\ "linkType": "HARD"\ + }],\ + ["npm:9.0.5", {\ + "packageLocation": "./.yarn/cache/minimatch-npm-9.0.5-9aa93d97fa-2c035575ed.zip/node_modules/minimatch/",\ + "packageDependencies": [\ + ["minimatch", "npm:9.0.5"],\ + ["brace-expansion", "npm:2.0.1"]\ + ],\ + "linkType": "HARD"\ }]\ ]],\ ["minimist", [\ @@ -19438,7 +19518,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/testing-library__jest-dom", "npm:5.14.5"],\ ["@types/uuid", "npm:9.0.1"],\ ["@typescript-eslint/eslint-plugin", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ - ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:7.5.0"],\ + ["@typescript-eslint/parser", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:8.17.0"],\ ["apollo-datasource-rest", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:3.7.0"],\ ["apollo-server-micro", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:3.11.1"],\ ["apollo3-cache-persist", "virtual:9909ff5388c6b6a3a46f12eb37c0afb449fcd1eedb9f02d871bde711a076c929583f48ecc4b85fa6d71478b076104a25f83dee45bc69687a22f551c576d7595d#npm:0.14.1"],\ @@ -24534,6 +24614,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ],\ "linkType": "SOFT"\ }],\ + ["npm:1.4.3", {\ + "packageLocation": "./.yarn/cache/ts-api-utils-npm-1.4.3-ee6b12ae73-ea00dee382.zip/node_modules/ts-api-utils/",\ + "packageDependencies": [\ + ["ts-api-utils", "npm:1.4.3"]\ + ],\ + "linkType": "SOFT"\ + }],\ ["virtual:0f7794eca8c536afb0e41b1bb750c7fef0f63fab3c0489648e5b096fef723275cc5288e236412fb5bd39bfd38f71a926240ed28d6b1219fd63acd084fb6ab940#npm:1.3.0", {\ "packageLocation": "./.yarn/__virtual__/ts-api-utils-virtual-e5182242d0/0/cache/ts-api-utils-npm-1.3.0-33457908f8-c746ddabfd.zip/node_modules/ts-api-utils/",\ "packageDependencies": [\ @@ -24547,6 +24634,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ],\ "linkType": "HARD"\ }],\ + ["virtual:ff45382f289877ead7011223e1a3e12ad5931bc5bb966ce16d645a7d92cd2edad32394d9fb08d21093dea26aa08aeb03e17de0977d68d796d54585998a88576c#npm:1.4.3", {\ + "packageLocation": "./.yarn/__virtual__/ts-api-utils-virtual-371d481ae5/0/cache/ts-api-utils-npm-1.4.3-ee6b12ae73-ea00dee382.zip/node_modules/ts-api-utils/",\ + "packageDependencies": [\ + ["ts-api-utils", "virtual:ff45382f289877ead7011223e1a3e12ad5931bc5bb966ce16d645a7d92cd2edad32394d9fb08d21093dea26aa08aeb03e17de0977d68d796d54585998a88576c#npm:1.4.3"],\ + ["@types/typescript", null],\ + ["typescript", "patch:typescript@npm%3A5.6.3#~builtin::version=5.6.3&hash=8c6c40"]\ + ],\ + "packagePeers": [\ + "@types/typescript",\ + "typescript"\ + ],\ + "linkType": "HARD"\ + }],\ ["virtual:ffbd544ad0fe91b6a84d26da1b983efebd6dadd9a2ae0160d3effbd386edafd313452293d39a630ddc390a351a5cc7679a4b023d895be27d9a3a0972f47b5e95#npm:1.3.0", {\ "packageLocation": "./.yarn/__virtual__/ts-api-utils-virtual-b61b2d1b10/0/cache/ts-api-utils-npm-1.3.0-33457908f8-c746ddabfd.zip/node_modules/ts-api-utils/",\ "packageDependencies": [\ diff --git a/.yarn/cache/@typescript-eslint-parser-npm-7.5.0-950f9b5b79-c9f85ae638.zip b/.yarn/cache/@typescript-eslint-parser-npm-7.5.0-950f9b5b79-c9f85ae638.zip deleted file mode 100644 index 2f26f80da..000000000 Binary files a/.yarn/cache/@typescript-eslint-parser-npm-7.5.0-950f9b5b79-c9f85ae638.zip and /dev/null differ diff --git a/.yarn/cache/@typescript-eslint-parser-npm-8.17.0-f09f5fe974-3d330fc777.zip b/.yarn/cache/@typescript-eslint-parser-npm-8.17.0-f09f5fe974-3d330fc777.zip new file mode 100644 index 000000000..ba8a603c9 Binary files /dev/null and b/.yarn/cache/@typescript-eslint-parser-npm-8.17.0-f09f5fe974-3d330fc777.zip differ diff --git a/.yarn/cache/@typescript-eslint-scope-manager-npm-8.17.0-f0be17d23c-c5f628e5b4.zip b/.yarn/cache/@typescript-eslint-scope-manager-npm-8.17.0-f0be17d23c-c5f628e5b4.zip new file mode 100644 index 000000000..e23c40f32 Binary files /dev/null and b/.yarn/cache/@typescript-eslint-scope-manager-npm-8.17.0-f0be17d23c-c5f628e5b4.zip differ diff --git a/.yarn/cache/@typescript-eslint-types-npm-8.17.0-ad6f5ae49f-5f6933903c.zip b/.yarn/cache/@typescript-eslint-types-npm-8.17.0-ad6f5ae49f-5f6933903c.zip new file mode 100644 index 000000000..2b7272d7a Binary files /dev/null and b/.yarn/cache/@typescript-eslint-types-npm-8.17.0-ad6f5ae49f-5f6933903c.zip differ diff --git a/.yarn/cache/@typescript-eslint-typescript-estree-npm-8.17.0-0110955b24-35d3dca3cd.zip b/.yarn/cache/@typescript-eslint-typescript-estree-npm-8.17.0-0110955b24-35d3dca3cd.zip new file mode 100644 index 000000000..bbaea79f5 Binary files /dev/null and b/.yarn/cache/@typescript-eslint-typescript-estree-npm-8.17.0-0110955b24-35d3dca3cd.zip differ diff --git a/.yarn/cache/@typescript-eslint-visitor-keys-npm-8.17.0-9c7a8b5908-f92f659ec8.zip b/.yarn/cache/@typescript-eslint-visitor-keys-npm-8.17.0-9c7a8b5908-f92f659ec8.zip new file mode 100644 index 000000000..e51f25820 Binary files /dev/null and b/.yarn/cache/@typescript-eslint-visitor-keys-npm-8.17.0-9c7a8b5908-f92f659ec8.zip differ diff --git a/.yarn/cache/eslint-visitor-keys-npm-4.2.0-a591434af3-779c604672.zip b/.yarn/cache/eslint-visitor-keys-npm-4.2.0-a591434af3-779c604672.zip new file mode 100644 index 000000000..29abd6d3c Binary files /dev/null and b/.yarn/cache/eslint-visitor-keys-npm-4.2.0-a591434af3-779c604672.zip differ diff --git a/.yarn/cache/fast-glob-npm-3.3.2-0a8cb4f2ca-900e4979f4.zip b/.yarn/cache/fast-glob-npm-3.3.2-0a8cb4f2ca-900e4979f4.zip new file mode 100644 index 000000000..409893e13 Binary files /dev/null and b/.yarn/cache/fast-glob-npm-3.3.2-0a8cb4f2ca-900e4979f4.zip differ diff --git a/.yarn/cache/minimatch-npm-9.0.5-9aa93d97fa-2c035575ed.zip b/.yarn/cache/minimatch-npm-9.0.5-9aa93d97fa-2c035575ed.zip new file mode 100644 index 000000000..7418c75bb Binary files /dev/null and b/.yarn/cache/minimatch-npm-9.0.5-9aa93d97fa-2c035575ed.zip differ diff --git a/.yarn/cache/ts-api-utils-npm-1.4.3-ee6b12ae73-ea00dee382.zip b/.yarn/cache/ts-api-utils-npm-1.4.3-ee6b12ae73-ea00dee382.zip new file mode 100644 index 000000000..f81d40f8a Binary files /dev/null and b/.yarn/cache/ts-api-utils-npm-1.4.3-ee6b12ae73-ea00dee382.zip differ diff --git a/package.json b/package.json index bc88f4e53..49cf054b2 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "@types/testing-library__jest-dom": "^5.14.5", "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^7.5.0", - "@typescript-eslint/parser": "^7.5.0", + "@typescript-eslint/parser": "^8.17.0", "concurrently": "^8.2.2", "cross-env": "^7.0.3", "css-mediaquery": "^0.1.2", diff --git a/pages/acceptInvite.page.tsx b/pages/acceptInvite.page.tsx index 689084ae3..b6d0d3f1d 100644 --- a/pages/acceptInvite.page.tsx +++ b/pages/acceptInvite.page.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { SetupPage } from 'src/components/Setup/SetupPage'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; import { useRequiredSession } from 'src/hooks/useRequiredSession'; -import { loadSession } from './api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from './api/utils/pagePropsHelpers'; interface FetchAcceptInviteProps { apiToken: string; @@ -149,6 +149,6 @@ const AcceptInvitePage = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default AcceptInvitePage; diff --git a/pages/accountLists/[accountListId].page.tsx b/pages/accountLists/[accountListId].page.tsx index b4461426d..e629549b8 100644 --- a/pages/accountLists/[accountListId].page.tsx +++ b/pages/accountLists/[accountListId].page.tsx @@ -5,7 +5,10 @@ import { GetDefaultAccountDocument, GetDefaultAccountQuery, } from 'pages/api/getDefaultAccount.generated'; -import { makeGetServerSideProps } from 'pages/api/utils/pagePropsHelpers'; +import { + handleUnderscoreAccountListRedirect, + makeGetServerSideProps, +} from 'pages/api/utils/pagePropsHelpers'; import { logErrorOnRollbar } from 'pages/api/utils/rollBar'; import Dashboard from 'src/components/Dashboard'; import { @@ -81,6 +84,14 @@ const AccountListIdPage = ({ export const getServerSideProps = makeGetServerSideProps( async (session, { query, req }) => { + const underscoreRedirect = await handleUnderscoreAccountListRedirect( + session, + req.url, + ); + if (underscoreRedirect) { + return underscoreRedirect; + } + const ssrClient = makeSsrClient(session.user.apiToken); try { diff --git a/pages/accountLists/[accountListId]/coaching.page.tsx b/pages/accountLists/[accountListId]/coaching.page.tsx index f1048302a..1b1164913 100644 --- a/pages/accountLists/[accountListId]/coaching.page.tsx +++ b/pages/accountLists/[accountListId]/coaching.page.tsx @@ -1,7 +1,7 @@ import Head from 'next/head'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { CoachingList } from 'src/components/Coaching/CoachingList'; import Loading from 'src/components/Loading'; import { useAccountListId } from 'src/hooks/useAccountListId'; @@ -26,6 +26,6 @@ const CoachingPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default CoachingPage; diff --git a/pages/accountLists/[accountListId]/coaching/[coachingId].page.tsx b/pages/accountLists/[accountListId]/coaching/[coachingId].page.tsx index 2e04917c8..e23a5ac3f 100644 --- a/pages/accountLists/[accountListId]/coaching/[coachingId].page.tsx +++ b/pages/accountLists/[accountListId]/coaching/[coachingId].page.tsx @@ -2,7 +2,7 @@ import Head from 'next/head'; import { useRouter } from 'next/router'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { AccountListTypeEnum, CoachingDetail, @@ -35,6 +35,6 @@ const CoachingPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default CoachingPage; diff --git a/pages/accountLists/[accountListId]/contacts/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/contacts/[[...contactId]].page.tsx index a34b0d66d..6c682bc93 100644 --- a/pages/accountLists/[accountListId]/contacts/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/contacts/[[...contactId]].page.tsx @@ -1,7 +1,7 @@ import Head from 'next/head'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { ContactsContext, ContactsType, @@ -72,4 +72,4 @@ const ContactsPage: React.FC = () => ( export default ContactsPage; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; diff --git a/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx b/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx index fd13d73e1..535207e69 100644 --- a/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx +++ b/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx @@ -8,7 +8,7 @@ import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { useTranslation } from 'react-i18next'; import { v4 as uuidv4 } from 'uuid'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { colorMap } from 'src/components/Contacts/ContactFlow/ContactFlow'; import { ContactFlowSetupColumn } from 'src/components/Contacts/ContactFlow/ContactFlowSetup/Column/ContactFlowSetupColumn'; import { UnusedStatusesColumn } from 'src/components/Contacts/ContactFlow/ContactFlowSetup/Column/UnusedStatusesColumn'; @@ -228,6 +228,6 @@ const ContactFlowSetupPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default ContactFlowSetupPage; diff --git a/pages/accountLists/[accountListId]/reports/coaching.page.tsx b/pages/accountLists/[accountListId]/reports/coaching.page.tsx index f571c5a51..4c9d30247 100644 --- a/pages/accountLists/[accountListId]/reports/coaching.page.tsx +++ b/pages/accountLists/[accountListId]/reports/coaching.page.tsx @@ -1,7 +1,7 @@ import Head from 'next/head'; import React, { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { AccountListTypeEnum, CoachingDetail, @@ -32,6 +32,6 @@ const CoachingReportPage = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default CoachingReportPage; diff --git a/pages/accountLists/[accountListId]/reports/designationAccounts.page.tsx b/pages/accountLists/[accountListId]/reports/designationAccounts.page.tsx index a6eed44d1..daa77879f 100644 --- a/pages/accountLists/[accountListId]/reports/designationAccounts.page.tsx +++ b/pages/accountLists/[accountListId]/reports/designationAccounts.page.tsx @@ -3,7 +3,7 @@ import React, { useState } from 'react'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; import { DesignationAccountsReport } from 'src/components/Reports/DesignationAccountsReport/DesignationAccountsReport'; @@ -67,6 +67,6 @@ const DesignationAccountsReportPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default DesignationAccountsReportPage; diff --git a/pages/accountLists/[accountListId]/reports/donations/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/reports/donations/[[...contactId]].page.tsx index 2aa777427..0c95bcf4a 100644 --- a/pages/accountLists/[accountListId]/reports/donations/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/reports/donations/[[...contactId]].page.tsx @@ -4,7 +4,7 @@ import React, { useState } from 'react'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { DynamicContactsRightPanel } from 'src/components/Contacts/ContactsRightPanel/DynamicContactsRightPanel'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; @@ -89,6 +89,6 @@ const DonationsReportPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default DonationsReportPage; diff --git a/pages/accountLists/[accountListId]/reports/expectedMonthlyTotal/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/reports/expectedMonthlyTotal/[[...contactId]].page.tsx index c92d16dd8..6d6fdd9df 100644 --- a/pages/accountLists/[accountListId]/reports/expectedMonthlyTotal/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/reports/expectedMonthlyTotal/[[...contactId]].page.tsx @@ -4,7 +4,7 @@ import React, { ReactElement, useState } from 'react'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { DynamicContactsRightPanel } from 'src/components/Contacts/ContactsRightPanel/DynamicContactsRightPanel'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; @@ -101,6 +101,6 @@ const ExpectedMonthlyTotalReportPage = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default ExpectedMonthlyTotalReportPage; diff --git a/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId].page.tsx b/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId].page.tsx index a87f35d4e..7683ca481 100644 --- a/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId].page.tsx +++ b/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId].page.tsx @@ -2,7 +2,7 @@ import Head from 'next/head'; import React, { useState } from 'react'; import { Box } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; import { AccountSummary } from 'src/components/Reports/FinancialAccountsReport/AccountSummary/AccountSummary'; @@ -61,6 +61,6 @@ const FinancialAccountSummaryPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FinancialAccountSummaryPage; diff --git a/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId]/entries.page.tsx b/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId]/entries.page.tsx index 1a048aa6e..024522b87 100644 --- a/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId]/entries.page.tsx +++ b/pages/accountLists/[accountListId]/reports/financialAccounts/[financialAccountId]/entries.page.tsx @@ -2,7 +2,7 @@ import Head from 'next/head'; import React, { ReactElement, useContext, useMemo } from 'react'; import { Box } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; import { AccountTransactions } from 'src/components/Reports/FinancialAccountsReport/AccountTransactions/AccountTransactions'; @@ -168,6 +168,6 @@ const FinancialAccountsPage: React.FC = () => ( ); -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FinancialAccountsPage; diff --git a/pages/accountLists/[accountListId]/reports/financialAccounts/index.page.tsx b/pages/accountLists/[accountListId]/reports/financialAccounts/index.page.tsx index 5a622673e..142e80b27 100644 --- a/pages/accountLists/[accountListId]/reports/financialAccounts/index.page.tsx +++ b/pages/accountLists/[accountListId]/reports/financialAccounts/index.page.tsx @@ -2,7 +2,7 @@ import Head from 'next/head'; import React, { useState } from 'react'; import { Box } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; import { FinancialAccounts } from 'src/components/Reports/FinancialAccountsReport/FinancialAccounts/FinancialAccounts'; @@ -64,6 +64,6 @@ const FinancialAccountsPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FinancialAccountsPage; diff --git a/pages/accountLists/[accountListId]/reports/partnerCurrency/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/reports/partnerCurrency/[[...contactId]].page.tsx index 2ad65a15e..c5d956666 100644 --- a/pages/accountLists/[accountListId]/reports/partnerCurrency/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/reports/partnerCurrency/[[...contactId]].page.tsx @@ -4,7 +4,7 @@ import React, { useState } from 'react'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { DynamicContactsRightPanel } from 'src/components/Contacts/ContactsRightPanel/DynamicContactsRightPanel'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; @@ -90,6 +90,6 @@ const PartnerCurrencyReportPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default PartnerCurrencyReportPage; diff --git a/pages/accountLists/[accountListId]/reports/partnerGivingAnalysis/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/reports/partnerGivingAnalysis/[[...contactId]].page.tsx index cb2a2c9ca..35d439837 100644 --- a/pages/accountLists/[accountListId]/reports/partnerGivingAnalysis/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/reports/partnerGivingAnalysis/[[...contactId]].page.tsx @@ -4,7 +4,7 @@ import React, { useMemo, useRef, useState } from 'react'; import { sortBy } from 'lodash'; import { useTranslation } from 'react-i18next'; import { ReportContactFilterSetInput } from 'pages/api/graphql-rest.page.generated'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { ContactsProvider } from 'src/components/Contacts/ContactsContext/ContactsContext'; import { DynamicContactsRightPanel } from 'src/components/Contacts/ContactsRightPanel/DynamicContactsRightPanel'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; @@ -174,6 +174,6 @@ const PartnerGivingAnalysisReportPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default PartnerGivingAnalysisReportPage; diff --git a/pages/accountLists/[accountListId]/reports/salaryCurrency/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/reports/salaryCurrency/[[...contactId]].page.tsx index de9716eb6..e4067613d 100644 --- a/pages/accountLists/[accountListId]/reports/salaryCurrency/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/reports/salaryCurrency/[[...contactId]].page.tsx @@ -4,7 +4,7 @@ import React, { useState } from 'react'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { DynamicContactsRightPanel } from 'src/components/Contacts/ContactsRightPanel/DynamicContactsRightPanel'; import { SidePanelsLayout } from 'src/components/Layouts/SidePanelsLayout'; import Loading from 'src/components/Loading'; @@ -90,6 +90,6 @@ const SalaryCurrencyReportPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default SalaryCurrencyReportPage; diff --git a/pages/accountLists/[accountListId]/settings/integrations/index.page.tsx b/pages/accountLists/[accountListId]/settings/integrations/index.page.tsx index e60ed30c8..944953180 100644 --- a/pages/accountLists/[accountListId]/settings/integrations/index.page.tsx +++ b/pages/accountLists/[accountListId]/settings/integrations/index.page.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; import { Button } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { ChalklineAccordion } from 'src/components/Settings/integrations/Chalkline/ChalklineAccordion'; import { GoogleAccordion } from 'src/components/Settings/integrations/Google/GoogleAccordion'; import { TheKeyAccordion } from 'src/components/Settings/integrations/Key/TheKeyAccordion'; @@ -120,6 +120,6 @@ const Integrations: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default Integrations; diff --git a/pages/accountLists/[accountListId]/settings/manageAccounts.page.tsx b/pages/accountLists/[accountListId]/settings/manageAccounts.page.tsx index 1e8de017b..cb42c0c7e 100644 --- a/pages/accountLists/[accountListId]/settings/manageAccounts.page.tsx +++ b/pages/accountLists/[accountListId]/settings/manageAccounts.page.tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import React, { ReactElement, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { ManageAccountAccessAccordion } from 'src/components/Settings/Accounts/ManageAccountAccess/ManageAccountAccessAccordion'; import { MergeAccountsAccordion } from 'src/components/Settings/Accounts/MergeAccounts/MergeAccountsAccordion'; import { MergeSpouseAccountsAccordion } from 'src/components/Settings/Accounts/MergeSpouseAccounts/MergeSpouseAccountsAccordion'; @@ -50,6 +50,6 @@ const ManageAccounts = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default ManageAccounts; diff --git a/pages/accountLists/[accountListId]/settings/manageCoaches.page.tsx b/pages/accountLists/[accountListId]/settings/manageCoaches.page.tsx index f154a57f9..607d2ceca 100644 --- a/pages/accountLists/[accountListId]/settings/manageCoaches.page.tsx +++ b/pages/accountLists/[accountListId]/settings/manageCoaches.page.tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import React, { ReactElement, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { ManageCoachesAccessAccordion } from 'src/components/Settings/Coaches/ManageCoachesAccess/ManageCoachesAccessAccordion'; import { AccordionGroup } from 'src/components/Shared/Forms/Accordions/AccordionGroup'; import { SettingsWrapper } from './Wrapper'; @@ -36,6 +36,6 @@ const ManageCoaching = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default ManageCoaching; diff --git a/pages/accountLists/[accountListId]/settings/notifications.page.tsx b/pages/accountLists/[accountListId]/settings/notifications.page.tsx index 3a94e0139..f6dfc4e28 100644 --- a/pages/accountLists/[accountListId]/settings/notifications.page.tsx +++ b/pages/accountLists/[accountListId]/settings/notifications.page.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import { Box, Button } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { NotificationsTable } from 'src/components/Settings/notifications/NotificationsTable'; import { SetupBanner } from 'src/components/Settings/preferences/SetupBanner'; import { useSetupContext } from 'src/components/Setup/SetupProvider'; @@ -78,6 +78,6 @@ const Notifications: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default Notifications; diff --git a/pages/accountLists/[accountListId]/settings/organizations.page.tsx b/pages/accountLists/[accountListId]/settings/organizations.page.tsx index da5951b0c..c2dbc4039 100644 --- a/pages/accountLists/[accountListId]/settings/organizations.page.tsx +++ b/pages/accountLists/[accountListId]/settings/organizations.page.tsx @@ -3,7 +3,7 @@ import React, { ReactElement, useEffect, useState } from 'react'; import { Autocomplete, Box, Skeleton, TextField } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { ImpersonateUserAccordion } from 'src/components/Settings/Organization/ImpersonateUser/ImpersonateUserAccordion'; import { ManageOrganizationAccessAccordion } from 'src/components/Settings/Organization/ManageOrganizationAccess/ManageOrganizationAccessAccordion'; import { AccordionGroup } from 'src/components/Shared/Forms/Accordions/AccordionGroup'; @@ -139,6 +139,6 @@ const Organizations = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default Organizations; diff --git a/pages/accountLists/[accountListId]/settings/organizations/accountLists.page.tsx b/pages/accountLists/[accountListId]/settings/organizations/accountLists.page.tsx index 8efcc3512..46602f767 100644 --- a/pages/accountLists/[accountListId]/settings/organizations/accountLists.page.tsx +++ b/pages/accountLists/[accountListId]/settings/organizations/accountLists.page.tsx @@ -11,7 +11,7 @@ import { import { styled } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { AccountLists } from 'src/components/Settings/Organization/AccountLists/AccountLists'; import { useDebouncedValue } from 'src/hooks/useDebounce'; import { SettingsWrapper } from '../Wrapper'; @@ -143,6 +143,6 @@ const AccountListsOrganizations = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default AccountListsOrganizations; diff --git a/pages/accountLists/[accountListId]/settings/organizations/contacts.page.tsx b/pages/accountLists/[accountListId]/settings/organizations/contacts.page.tsx index 77e9faa26..43ac26f12 100644 --- a/pages/accountLists/[accountListId]/settings/organizations/contacts.page.tsx +++ b/pages/accountLists/[accountListId]/settings/organizations/contacts.page.tsx @@ -11,7 +11,7 @@ import { import { styled } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { Contacts } from 'src/components/Settings/Organization/Contacts/Contacts'; import { useDebouncedValue } from 'src/hooks/useDebounce'; import { SettingsWrapper } from '../Wrapper'; @@ -130,6 +130,6 @@ const OrganizationsContacts = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default OrganizationsContacts; diff --git a/pages/accountLists/[accountListId]/settings/preferences.page.tsx b/pages/accountLists/[accountListId]/settings/preferences.page.tsx index 9733ef0fa..cac9198f1 100644 --- a/pages/accountLists/[accountListId]/settings/preferences.page.tsx +++ b/pages/accountLists/[accountListId]/settings/preferences.page.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react'; import { Box, Button, Skeleton } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { useGetUsersOrganizationsAccountsQuery } from 'src/components/Settings/integrations/Organization/Organizations.generated'; import { useCanUserExportDataQuery, @@ -343,6 +343,6 @@ const Preferences: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default Preferences; diff --git a/pages/accountLists/[accountListId]/setup/finish.page.tsx b/pages/accountLists/[accountListId]/setup/finish.page.tsx index ec69bb5b4..bc211b13f 100644 --- a/pages/accountLists/[accountListId]/setup/finish.page.tsx +++ b/pages/accountLists/[accountListId]/setup/finish.page.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import React, { useEffect } from 'react'; import { Button } from '@mui/material'; import { Trans, useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { SetupPage } from 'src/components/Setup/SetupPage'; import { LargeButton } from 'src/components/Setup/styledComponents'; import { useAccountListId } from 'src/hooks/useAccountListId'; @@ -73,6 +73,6 @@ You can import from software like TntConnect, Google Contacts or a Spreadsheet.` ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FinishPage; diff --git a/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx index 156b88239..2f106213f 100644 --- a/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tasks/[[...contactId]].page.tsx @@ -7,7 +7,7 @@ import { Box, Button, ButtonGroup, Hidden } from '@mui/material'; import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { ContactsProvider } from 'src/components/Contacts/ContactsContext/ContactsContext'; import { DynamicContactsRightPanel } from 'src/components/Contacts/ContactsRightPanel/DynamicContactsRightPanel'; import { InfiniteList } from 'src/components/InfiniteList/InfiniteList'; @@ -411,6 +411,6 @@ const TasksPage: React.FC = () => { //#endregion }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default TasksPage; diff --git a/pages/accountLists/[accountListId]/tools.page.tsx b/pages/accountLists/[accountListId]/tools.page.tsx index 004ea8df9..ba6f3aac8 100644 --- a/pages/accountLists/[accountListId]/tools.page.tsx +++ b/pages/accountLists/[accountListId]/tools.page.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/router'; import React, { ReactElement } from 'react'; import { Button } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { SetupBanner } from 'src/components/Settings/preferences/SetupBanner'; import { StickyBox } from 'src/components/Shared/Header/styledComponents'; import ToolsHome from 'src/components/Tool/Home/ToolsHome'; @@ -43,6 +43,6 @@ const ToolsPage = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default ToolsPage; diff --git a/pages/accountLists/[accountListId]/tools/appeals/appeal/[[...appealId]].page.tsx b/pages/accountLists/[accountListId]/tools/appeals/appeal/[[...appealId]].page.tsx index 57d6a7c4c..80f94a33a 100644 --- a/pages/accountLists/[accountListId]/tools/appeals/appeal/[[...appealId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/appeals/appeal/[[...appealId]].page.tsx @@ -1,6 +1,6 @@ import React, { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import AppealsDetailsPage from 'src/components/Tool/Appeal/AppealDetails/AppealsDetailsPage'; import { ToolsWrapper } from '../../ToolsWrapper'; import { AppealsWrapper } from '../AppealsWrapper'; @@ -29,4 +29,4 @@ const AppealsPage: React.FC = () => ( export default AppealsPage; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; diff --git a/pages/accountLists/[accountListId]/tools/appeals/index.page.tsx b/pages/accountLists/[accountListId]/tools/appeals/index.page.tsx index 9c6791897..f3af95196 100644 --- a/pages/accountLists/[accountListId]/tools/appeals/index.page.tsx +++ b/pages/accountLists/[accountListId]/tools/appeals/index.page.tsx @@ -1,6 +1,6 @@ import React, { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import AppealsInitialPage from 'src/components/Tool/Appeal/InitialPage/AppealsInitialPage'; import { ToolsWrapper } from '../ToolsWrapper'; @@ -21,4 +21,4 @@ const AppealsPage = (): ReactElement => { export default AppealsPage; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; diff --git a/pages/accountLists/[accountListId]/tools/fix/commitmentInfo/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tools/fix/commitmentInfo/[[...contactId]].page.tsx index 4cd1fae91..b93b79452 100644 --- a/pages/accountLists/[accountListId]/tools/fix/commitmentInfo/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/fix/commitmentInfo/[[...contactId]].page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import FixCommitmentInfo from 'src/components/Tool/FixCommitmentInfo/FixCommitmentInfo'; import { ToolsWrapper } from '../../ToolsWrapper'; import { useToolsHelper } from '../../useToolsHelper'; @@ -21,6 +21,6 @@ const FixCommitmentInfoPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FixCommitmentInfoPage; diff --git a/pages/accountLists/[accountListId]/tools/fix/emailAddresses/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tools/fix/emailAddresses/[[...contactId]].page.tsx index 43ab3189b..435338f1c 100644 --- a/pages/accountLists/[accountListId]/tools/fix/emailAddresses/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/fix/emailAddresses/[[...contactId]].page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { FixEmailAddresses } from 'src/components/Tool/FixEmailAddresses/FixEmailAddresses'; import { ToolsWrapper } from '../../ToolsWrapper'; import { useToolsHelper } from '../../useToolsHelper'; @@ -21,6 +21,6 @@ const FixEmailAddressesPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FixEmailAddressesPage; diff --git a/pages/accountLists/[accountListId]/tools/fix/mailingAddresses/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tools/fix/mailingAddresses/[[...contactId]].page.tsx index e648ba975..8e5c4931a 100644 --- a/pages/accountLists/[accountListId]/tools/fix/mailingAddresses/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/fix/mailingAddresses/[[...contactId]].page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import FixMailingAddresses from 'src/components/Tool/FixMailingAddresses/FixMailingAddresses'; import { ToolsWrapper } from '../../ToolsWrapper'; import { useToolsHelper } from '../../useToolsHelper'; @@ -21,6 +21,6 @@ const FixMailingAddressesPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FixMailingAddressesPage; diff --git a/pages/accountLists/[accountListId]/tools/fix/phoneNumbers/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tools/fix/phoneNumbers/[[...contactId]].page.tsx index ca7619c2b..f1ce3e9e2 100644 --- a/pages/accountLists/[accountListId]/tools/fix/phoneNumbers/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/fix/phoneNumbers/[[...contactId]].page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import FixPhoneNumbers from 'src/components/Tool/FixPhoneNumbers/FixPhoneNumbers'; import { ToolsWrapper } from '../../ToolsWrapper'; import { useToolsHelper } from '../../useToolsHelper'; @@ -21,6 +21,6 @@ const FixPhoneNumbersPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FixPhoneNumbersPage; diff --git a/pages/accountLists/[accountListId]/tools/fix/sendNewsletter/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tools/fix/sendNewsletter/[[...contactId]].page.tsx index 3e05d1fdf..952dc668b 100644 --- a/pages/accountLists/[accountListId]/tools/fix/sendNewsletter/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/fix/sendNewsletter/[[...contactId]].page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import FixSendNewsletter from 'src/components/Tool/FixSendNewsletter/FixSendNewsletter'; import { ToolsWrapper } from '../../ToolsWrapper'; import { useToolsHelper } from '../../useToolsHelper'; @@ -21,6 +21,6 @@ const FixSendNewsletterPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default FixSendNewsletterPage; diff --git a/pages/accountLists/[accountListId]/tools/import/csv.page.tsx b/pages/accountLists/[accountListId]/tools/import/csv.page.tsx index dd110199e..eb9eead5d 100644 --- a/pages/accountLists/[accountListId]/tools/import/csv.page.tsx +++ b/pages/accountLists/[accountListId]/tools/import/csv.page.tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { CsvImportProvider, CsvImportViewStepEnum, @@ -78,6 +78,6 @@ const CsvHome: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default CsvHome; diff --git a/pages/accountLists/[accountListId]/tools/import/google.page.tsx b/pages/accountLists/[accountListId]/tools/import/google.page.tsx index 197d6a070..f6107fc39 100644 --- a/pages/accountLists/[accountListId]/tools/import/google.page.tsx +++ b/pages/accountLists/[accountListId]/tools/import/google.page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import Loading from 'src/components/Loading'; import GoogleImport from 'src/components/Tool/GoogleImport/GoogleImport'; import { useAccountListId } from 'src/hooks/useAccountListId'; @@ -26,6 +26,6 @@ const GoogleImportPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default GoogleImportPage; diff --git a/pages/accountLists/[accountListId]/tools/import/tnt.page.tsx b/pages/accountLists/[accountListId]/tools/import/tnt.page.tsx index 2cff3e1ef..4f9f5f23f 100644 --- a/pages/accountLists/[accountListId]/tools/import/tnt.page.tsx +++ b/pages/accountLists/[accountListId]/tools/import/tnt.page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import Loading from 'src/components/Loading'; import TntConnect from 'src/components/Tool/TntConnect/TntConnect'; import { useAccountListId } from 'src/hooks/useAccountListId'; @@ -26,6 +26,6 @@ const TntConnectPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default TntConnectPage; diff --git a/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.tsx index 67f9b51f2..c738a4830 100644 --- a/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import MergeContacts from 'src/components/Tool/MergeContacts/MergeContacts'; import { ToolsWrapper } from '../../ToolsWrapper'; import { useToolsHelper } from '../../useToolsHelper'; @@ -28,6 +28,6 @@ const MergeContactsPage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default MergeContactsPage; diff --git a/pages/accountLists/[accountListId]/tools/merge/people/[[...contactId]].page.tsx b/pages/accountLists/[accountListId]/tools/merge/people/[[...contactId]].page.tsx index 95f9ce294..1960c30dc 100644 --- a/pages/accountLists/[accountListId]/tools/merge/people/[[...contactId]].page.tsx +++ b/pages/accountLists/[accountListId]/tools/merge/people/[[...contactId]].page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { loadSession } from 'pages/api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import MergePeople from 'src/components/Tool/MergePeople/MergePeople'; import { ToolsWrapper } from '../../ToolsWrapper'; import { useToolsHelper } from '../../useToolsHelper'; @@ -21,6 +21,6 @@ const MergePeoplePage: React.FC = () => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default MergePeoplePage; diff --git a/pages/api/graphql-rest.page.ts b/pages/api/graphql-rest.page.ts index a383dbca2..bb00a70ba 100644 --- a/pages/api/graphql-rest.page.ts +++ b/pages/api/graphql-rest.page.ts @@ -993,10 +993,22 @@ class MpdxRestApi extends RESTDataSource { googleIntegrationId, googleIntegration, ) { - const attributes = {}; + interface GoogleIntegrationAttributes { + overwrite?: boolean; + calendar_id?: string; + calendar_integrations?: string[]; + } + const attributes: GoogleIntegrationAttributes = {}; Object.keys(googleIntegration).map((key) => { attributes[camelToSnake(key)] = googleIntegration[key]; }); + if (attributes?.calendar_integrations) { + // Convert to lowercase since we convert them from lowercase to Uppercase + // when we fetch initially from pages/api/Schema/Settings/Integrations/Google/parse.ts + attributes.calendar_integrations = attributes?.calendar_integrations.map( + (integration) => integration.toLowerCase(), + ); + } const { data }: { data: GoogleIntegrationResponse } = await this.put( `user/google_accounts/${googleAccountId}/google_integrations/${googleIntegrationId}`, diff --git a/pages/api/utils/pagePropsHelpers.test.ts b/pages/api/utils/pagePropsHelpers.test.ts index a6c1fbdaf..6d09d07f5 100644 --- a/pages/api/utils/pagePropsHelpers.test.ts +++ b/pages/api/utils/pagePropsHelpers.test.ts @@ -1,140 +1,192 @@ import { GetServerSidePropsContext } from 'next'; import { getSession } from 'next-auth/react'; import { session } from '__tests__/fixtures/session'; +import makeSsrClient from 'src/lib/apollo/ssrClient'; import { enforceAdmin, - loadSession, + ensureSessionAndAccountList, loginRedirect, makeGetServerSideProps, } from './pagePropsHelpers'; jest.mock('next-auth/react'); +jest.mock('src/lib/apollo/ssrClient', () => jest.fn()); const context = { + req: {}, query: { accountListId: 'account-list-1' }, resolvedUrl: '/page?param=value', } as unknown as GetServerSidePropsContext; -describe('loginRedirect', () => { - it('returns redirect with current URL', () => { - expect(loginRedirect(context)).toEqual({ - redirect: { - destination: '/login?redirect=%2Fpage%3Fparam%3Dvalue', - permanent: false, - }, +describe('pagePropsHelpers', () => { + describe('loginRedirect', () => { + it('returns redirect with current URL', () => { + expect(loginRedirect(context)).toEqual({ + redirect: { + destination: '/login?redirect=%2Fpage%3Fparam%3Dvalue', + permanent: false, + }, + }); }); }); -}); -describe('enforceAdmin', () => { - it('does not return a redirect if the user is an admin', async () => { - (getSession as jest.Mock).mockResolvedValue({ user: { admin: true } }); + describe('enforceAdmin', () => { + it('does not return a redirect if the user is an admin', async () => { + (getSession as jest.Mock).mockResolvedValue({ user: { admin: true } }); - await expect(enforceAdmin(context)).resolves.not.toMatchObject({ - redirect: {}, + await expect(enforceAdmin(context)).resolves.not.toMatchObject({ + redirect: {}, + }); }); - }); - it('returns a redirect if the user is not an admin', async () => { - (getSession as jest.Mock).mockResolvedValue({ user: { admin: false } }); + it('returns a redirect if the user is not an admin', async () => { + (getSession as jest.Mock).mockResolvedValue({ user: { admin: false } }); - await expect(enforceAdmin(context)).resolves.toMatchObject({ - redirect: { - destination: '/accountLists/account-list-1', - }, + await expect(enforceAdmin(context)).resolves.toMatchObject({ + redirect: { + destination: '/accountLists/account-list-1', + }, + }); }); }); -}); - -describe('loadSession', () => { - it('does not return a redirect if the user is logged in', async () => { - const user = { apiToken: 'token' }; - (getSession as jest.Mock).mockResolvedValue({ user }); - await expect(loadSession(context)).resolves.toMatchObject({ - props: { - session: { user }, - }, + describe('ensureSessionAndAccountList', () => { + it('does not return a redirect if the user is logged in', async () => { + const user = { apiToken: 'token' }; + (getSession as jest.Mock).mockResolvedValue({ user }); + + await expect(ensureSessionAndAccountList(context)).resolves.toMatchObject( + { + props: { + session: { user }, + }, + }, + ); }); - }); - it('returns a redirect if the user is not logged in', async () => { - (getSession as jest.Mock).mockResolvedValue(null); + it('returns a redirect if the user is not logged in', async () => { + (getSession as jest.Mock).mockResolvedValue(null); - await expect(loadSession(context)).resolves.toMatchObject({ - redirect: { - destination: '/login?redirect=%2Fpage%3Fparam%3Dvalue', - }, + await expect(ensureSessionAndAccountList(context)).resolves.toMatchObject( + { + redirect: { + destination: '/login?redirect=%2Fpage%3Fparam%3Dvalue', + }, + }, + ); }); - }); -}); -describe('makeGetServerSideProps', () => { - it('redirects to the login page if the session is missing', async () => { - (getSession as jest.Mock).mockResolvedValue(null); - - const getServerSidePropsFromSession = jest.fn(); - const getServerSideProps = makeGetServerSideProps( - getServerSidePropsFromSession, - ); - - await expect(getServerSideProps(context)).resolves.toEqual({ - redirect: { - destination: '/login?redirect=%2Fpage%3Fparam%3Dvalue', - permanent: false, - }, + describe('redirects to the default account list if the URL contains "_"', () => { + const context = { + req: { url: '/accountLists/_/contacts' }, + resolvedUrl: '/filters?param=value', + } as unknown as GetServerSidePropsContext; + beforeEach(() => { + const user = { apiToken: 'token' }; + (getSession as jest.Mock).mockResolvedValue({ user }); + const query = jest.fn().mockResolvedValueOnce({ + data: { + user: { + defaultAccountList: 'defaultAccountList', + }, + }, + }); + (makeSsrClient as jest.Mock).mockReturnValue({ + query: query, + }); + }); + + it('redirects to the contacts page with default account list', async () => { + await expect( + ensureSessionAndAccountList(context), + ).resolves.toMatchObject({ + redirect: { + destination: '/accountLists/defaultAccountList/contacts', + }, + }); + }); + + it('redirects to dashboard with default account list"', async () => { + await expect( + ensureSessionAndAccountList({ + req: { url: '/accountLists/_' }, + } as unknown as GetServerSidePropsContext), + ).resolves.toMatchObject({ + redirect: { + destination: '/accountLists/defaultAccountList', + }, + }); + }); }); - expect(getServerSidePropsFromSession).not.toHaveBeenCalled(); }); - it('calls the custom function and adds the session to the returned props', async () => { - (getSession as jest.Mock).mockResolvedValue(session); - - const getServerSidePropsFromSession = jest.fn().mockResolvedValue({ - props: { - data1: 1, - dataA: 'A', - }, + describe('makeGetServerSideProps', () => { + it('redirects to the login page if the session is missing', async () => { + (getSession as jest.Mock).mockResolvedValue(null); + + const getServerSidePropsFromSession = jest.fn(); + const getServerSideProps = makeGetServerSideProps( + getServerSidePropsFromSession, + ); + + await expect(getServerSideProps(context)).resolves.toEqual({ + redirect: { + destination: '/login?redirect=%2Fpage%3Fparam%3Dvalue', + permanent: false, + }, + }); + expect(getServerSidePropsFromSession).not.toHaveBeenCalled(); }); - const getServerSideProps = makeGetServerSideProps( - getServerSidePropsFromSession, - ); - await expect(getServerSideProps(context)).resolves.toEqual({ - props: { + it('calls the custom function and adds the session to the returned props', async () => { + (getSession as jest.Mock).mockResolvedValue(session); + + const getServerSidePropsFromSession = jest.fn().mockResolvedValue({ + props: { + data1: 1, + dataA: 'A', + }, + }); + const getServerSideProps = makeGetServerSideProps( + getServerSidePropsFromSession, + ); + + await expect(getServerSideProps(context)).resolves.toEqual({ + props: { + session, + data1: 1, + dataA: 'A', + }, + }); + expect(getServerSidePropsFromSession).toHaveBeenCalledWith( session, - data1: 1, - dataA: 'A', - }, + context, + ); }); - expect(getServerSidePropsFromSession).toHaveBeenCalledWith( - session, - context, - ); - }); - it('calls the custom function and passes through redirects', async () => { - (getSession as jest.Mock).mockResolvedValue(session); - - const getServerSidePropsFromSession = jest.fn().mockResolvedValue({ - redirect: { - destination: '/new/url', - permanent: false, - }, - }); - const getServerSideProps = makeGetServerSideProps( - getServerSidePropsFromSession, - ); - - await expect(getServerSideProps(context)).resolves.toEqual({ - redirect: { - destination: '/new/url', - permanent: false, - }, + it('calls the custom function and passes through redirects', async () => { + (getSession as jest.Mock).mockResolvedValue(session); + + const getServerSidePropsFromSession = jest.fn().mockResolvedValue({ + redirect: { + destination: '/new/url', + permanent: false, + }, + }); + const getServerSideProps = makeGetServerSideProps( + getServerSidePropsFromSession, + ); + + await expect(getServerSideProps(context)).resolves.toEqual({ + redirect: { + destination: '/new/url', + permanent: false, + }, + }); + expect(getServerSidePropsFromSession).toHaveBeenCalledWith( + session, + context, + ); }); - expect(getServerSidePropsFromSession).toHaveBeenCalledWith( - session, - context, - ); }); }); diff --git a/pages/api/utils/pagePropsHelpers.ts b/pages/api/utils/pagePropsHelpers.ts index 00f3e503d..546cb6666 100644 --- a/pages/api/utils/pagePropsHelpers.ts +++ b/pages/api/utils/pagePropsHelpers.ts @@ -5,6 +5,11 @@ import { } from 'next'; import { Session } from 'next-auth'; import { getSession } from 'next-auth/react'; +import makeSsrClient from 'src/lib/apollo/ssrClient'; +import { + GetDefaultAccountDocument, + GetDefaultAccountQuery, +} from '../getDefaultAccount.generated'; interface PagePropsWithSession { session: Session; @@ -21,9 +26,9 @@ export const loginRedirect = ( }); // Redirect back to the dashboard if the user isn't an admin -export const enforceAdmin: GetServerSideProps = async ( +export const enforceAdmin = async ( context, -) => { +): Promise> => { const session = await getSession(context); if (!session?.user.admin) { return { @@ -33,6 +38,15 @@ export const enforceAdmin: GetServerSideProps = async ( }, }; } + + const underscoreRedirect = await handleUnderscoreAccountListRedirect( + session, + context.req.url, + ); + if (underscoreRedirect) { + return underscoreRedirect; + } + return { props: { session, @@ -41,12 +55,22 @@ export const enforceAdmin: GetServerSideProps = async ( }; // Redirect back to login screen if user isn't logged in -export const loadSession: GetServerSideProps = async ( +export const ensureSessionAndAccountList = async ( context, -) => { +): Promise> => { const session = await getSession(context); if (!session?.user.apiToken) { - return loginRedirect(context); + return { + ...loginRedirect(context), + }; + } + + const underscoreRedirect = await handleUnderscoreAccountListRedirect( + session, + context.req.url, + ); + if (underscoreRedirect) { + return underscoreRedirect; } return { @@ -56,6 +80,32 @@ export const loadSession: GetServerSideProps = async ( }; }; +export const handleUnderscoreAccountListRedirect = async ( + session: Session, + url?: string, +): Promise | undefined> => { + if (url?.includes('/accountLists/_')) { + // Redirect to the default account list if the "_" is where the account list ID would be in the URL + // This is a common pattern in our app, so we handle it here to avoid repeating + const ssrClient = makeSsrClient(session.user.apiToken); + const { data } = await ssrClient.query({ + query: GetDefaultAccountDocument, + }); + + if (data.user.defaultAccountList) { + return { + redirect: { + destination: url.replace( + '/accountLists/_', + `/accountLists/${data.user.defaultAccountList}`, + ), + permanent: false, + }, + }; + } + } +}; + /** * It is a common pattern in `getServerSideProps` to need to first extract the * API token from the session, redirect to the login page if the API token is diff --git a/pages/logout.page.tsx b/pages/logout.page.tsx index 448272fcc..ee7b20d92 100644 --- a/pages/logout.page.tsx +++ b/pages/logout.page.tsx @@ -8,7 +8,7 @@ import { signOut } from 'next-auth/react'; import { useTranslation } from 'react-i18next'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; import { clearDataDogUser } from 'src/lib/dataDog'; -import { loadSession } from './api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from './api/utils/pagePropsHelpers'; const BoxWrapper = styled(Box)(({ theme }) => ({ backgroundColor: theme.palette.cruGrayLight.main, @@ -51,6 +51,6 @@ const LogoutPage = ({}): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default LogoutPage; diff --git a/pages/setup/connect.page.tsx b/pages/setup/connect.page.tsx index 14cf7ff63..cc6170fe5 100644 --- a/pages/setup/connect.page.tsx +++ b/pages/setup/connect.page.tsx @@ -3,7 +3,7 @@ import React, { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import { Connect } from 'src/components/Setup/Connect'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; -import { loadSession } from '../api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from '../api/utils/pagePropsHelpers'; // This is the second page of the setup tour. It lets users connect to // organizations. It will be shown if the user doesn't have any organization @@ -22,6 +22,6 @@ const ConnectPage = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default ConnectPage; diff --git a/pages/setup/start.page.tsx b/pages/setup/start.page.tsx index 60fb266de..0dfcf0871 100644 --- a/pages/setup/start.page.tsx +++ b/pages/setup/start.page.tsx @@ -12,7 +12,7 @@ import { } from 'src/components/Shared/Links/Links'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; import { formatLanguage, languages } from 'src/lib/data/languages'; -import { loadSession } from '../api/utils/pagePropsHelpers'; +import { ensureSessionAndAccountList } from '../api/utils/pagePropsHelpers'; // This is the first page of the tour, and it lets users choose their language. It is always shown. const StartPage = (): ReactElement => { @@ -92,6 +92,6 @@ const StartPage = (): ReactElement => { ); }; -export const getServerSideProps = loadSession; +export const getServerSideProps = ensureSessionAndAccountList; export default StartPage; diff --git a/public/locales/ar/translation.json b/public/locales/ar/translation.json index 7edfadb16..004789901 100644 --- a/public/locales/ar/translation.json +++ b/public/locales/ar/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "لم يعد العنوان صالحا", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "إدارة %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "لوحة التسيير", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "سيتم نسخ البيانات من \"الخاسرين\" إلى \"الفائزين\".اختر الفائز في الأسفل. لن تضيع أية بيانات بالدمج.", + "Data sources:": "Data sources:", "DataServer": "خادم البيانات", "Date": "التاريخ", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "خدمات التبرع عبر البريد الالكتروني هنا", "Email Newsletter List": "إرسال قائمة النشرة", + "Email: ": "Email: ", "Employer": "رب عمل", "Enable Calendar Integration": "فعّل دمج التقويم", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "أرقام الهاتف", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "مادي", "PHYSICAL": "مادي", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/de-CH/translation.json b/public/locales/de-CH/translation.json index 23515ba14..734dfe73c 100644 --- a/public/locales/de-CH/translation.json +++ b/public/locales/de-CH/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Address no longer valid", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Dashboard", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.", + "Data sources:": "Data sources:", "DataServer": "DataServer", "Date": "Date", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Email Donation Services here", "Email Newsletter List": "Email Newsletter List", + "Email: ": "Email: ", "Employer": "Employer", "Enable Calendar Integration": "Enable Calendar Integration", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Phone Numbers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Physical", "PHYSICAL": "Physical", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 50064ebe1..307704c16 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Adresse erfolgreich gelöscht", "Address no longer valid": "Adresse nicht mehr gültig", "Address updated successfully": "Adresse erfolgreich aktualisiert", + "Address: ": "Address: ", "Addresses": "Adressen", "Addresses will be formatted based on country. (Experimental)": "Die Adressen werden basierend auf den Ländern formatiert. (Experimentell)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesch", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Basierend auf einer Analyse des Spendenverlaufs eines Partners kann {{appName}} Sie über Ereignisse benachrichtigen, die Sie vermutlich nachverfolgen möchten. Die Erkennungslogik basiert auf einer Reihe von Regeln, die meistens korrekt sind, aber Sie sollten trotzdem ein Ereignis manuell bestätigen, bevor Sie den Partner kontaktieren.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgien", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Tschechische Republik", "Dashboard": "Dashboard", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Daten der „abgebenden Person“ werden zu der „aufnehmenden Person“ kopiert. Wählen Sie unten die aufnehmende Person. Bei der Zusammenführung gehen keine Daten verloren.", + "Data sources:": "Data sources:", "DataServer": "Datenserver", "Date": "Datum", "Date Committed": "Datum der Zusage", @@ -830,6 +833,7 @@ "Email Address Type": "Typ der E-Mail-Adresse", "Email Donation Services here": "E-Mail an Donation Services senden", "Email Newsletter List": "Liste für E-Mail-Rundbrief", + "Email: ": "Email: ", "Employer": "Arbeitgeber", "Enable Calendar Integration": "Kalenderverknüpfung aktivieren", "Enabled Google Calendar Integration!": "Google-Kalender-Integration aktiviert!", @@ -946,6 +950,7 @@ "Follow up": "Folgenachricht", "Follow Up for Decision": "Nachgehen bezüglich Entscheidung", "Follow-Up": "Nachbereitung", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "{{appName}} kann dich über jedes Ereignis per E-Mail benachrichtigen und außerdem eine Aufgabe zur Erinnerung erstellen. Mit den Optionen unten kannst du die Einstellungen dafür ändern.", "Foreign Amount": "Betrag in Fremdwährung", "Fourteen month report table": "14-Monats-Bericht", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Art der Telefonnummer", "Phone Numbers": "Telefonnummern", "Phone numbers updated!": "Telefonnummer aktualisiert!", + "Phone: ": "Phone: ", "Physical": "Physisch", "PHYSICAL": "Physisch", "Physical Newsletter": "Rundbrief - Papier", diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 44b2f68df..75c5522db 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -320,6 +320,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Address no longer valid", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -434,6 +435,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -624,6 +626,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Dashboard", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.", + "Data sources:": "Data sources:", "Date": "Date", "Date of Connection": "Date of Connection", "Days": "Days", @@ -727,6 +730,7 @@ "Email Address": "Email Address", "Email Address Type": "Email Address Type", "Email Donation Services here": "Email Donation Services here", + "Email: ": "Email: ", "Employer": "Employer", "Enable Calendar Integration": "Enable Calendar Integration", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -828,6 +832,7 @@ "Fix Send Newsletter": "Fix Send Newsletter", "Flows View": "Flows View", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1346,6 +1351,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Phone Numbers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Physical", "Physical Newsletter": "Physical Newsletter", "Pick a default set of columns to use": "Pick a default set of columns to use", diff --git a/public/locales/es-419/translation.json b/public/locales/es-419/translation.json index 3b418a7aa..21a3f315c 100644 --- a/public/locales/es-419/translation.json +++ b/public/locales/es-419/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Donación eliminada correctamente", "Address no longer valid": "La dirección ya no es válida", "Address updated successfully": "Dirección actualizada correctamente", + "Address: ": "Address: ", "Addresses": "Direcciones", "Addresses will be formatted based on country. (Experimental)": "El formato de las direcciones variará según el país. (Experimental)", "Admin %": "% de admin.", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "En base a un análisis del historial de donaciones de un socio, {{appName}} puede brindarle notificaciones sobre eventos que probablemente querrá seguir. La lógica de detección está basada en un conjunto de reglas que son correctas la mayoría del tiempo, pero se recomienda verificar un evento manualmente antes de contactar al socio.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Bielorrusia", "Belgium": "Bélgica", "Belize": "Belice", @@ -710,6 +712,7 @@ "Czech Republic": "República Checa", "Dashboard": "Panel", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Los datos de los \"perdedores\" se copiarán en el \"ganador\". Seleccione al ganador a continuación. No se perderán datos por combinación.", + "Data sources:": "Data sources:", "DataServer": "Dataserver", "Date": "Guardar", "Date Committed": "Fecha comprometida", @@ -830,6 +833,7 @@ "Email Address Type": "Tipo de dirección de correo electrónico", "Email Donation Services here": "Enviar servicios de donación por correo electrónico aquí", "Email Newsletter List": "Lista correos electrónicos para la carta de noticias.", + "Email: ": "Email: ", "Employer": "Empleador", "Enable Calendar Integration": "Habilitar Integración con calendario", "Enabled Google Calendar Integration!": "Habilitar Integración con calendario", @@ -946,6 +950,7 @@ "Follow up": "Seguimiento", "Follow Up for Decision": "Realizar un seguimiento para una decisión", "Follow-Up": "Seguimiento", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "Para cada evento, {{appName}} puede enviarle una notificación por correo electrónico y también puede crear una entrada de tarea como recordatorio para alguna tarea del evento. Las siguientes opciones le permiten controlar ese comportamiento.", "Foreign Amount": "Monto extranjero", "Fourteen month report table": "Tabla de informe de catorce meses", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Tipo de número de teléfono", "Phone Numbers": "Números de teléfono", "Phone numbers updated!": "¡Números de teléfono actualizados!", + "Phone: ": "Phone: ", "Physical": "Físico", "PHYSICAL": "Físico", "Physical Newsletter": "Boletín informativo (impreso)", diff --git a/public/locales/fr-CA/translation.json b/public/locales/fr-CA/translation.json index efdc8b83e..50be08a0d 100644 --- a/public/locales/fr-CA/translation.json +++ b/public/locales/fr-CA/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Address no longer valid", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Dashboard", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.", + "Data sources:": "Data sources:", "DataServer": "DataServer", "Date": "Date", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Email Donation Services here", "Email Newsletter List": "Email Newsletter List", + "Email: ": "Email: ", "Employer": "Employer", "Enable Calendar Integration": "Enable Calendar Integration", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Phone Numbers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Physical", "PHYSICAL": "Physical", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/fr-FR/translation.json b/public/locales/fr-FR/translation.json index 6c787f9a3..c5862eddf 100644 --- a/public/locales/fr-FR/translation.json +++ b/public/locales/fr-FR/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Adresse supprimée avec succès", "Address no longer valid": "Cette adresse n’est plus valide", "Address updated successfully": "Adresse mise à jour avec succès", + "Address: ": "Address: ", "Addresses": "Adresses", "Addresses will be formatted based on country. (Experimental)": "Les adresses seront formatées en fonction du pays. (expérimental)", "Admin %": "% de frais administratifs", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Tableau de bord", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": " Les données des « contacts secondaires » seront copiées dans le « contact principal ». Sélectionnez le contact principal ci-dessous. Aucune donnée ne sera perdue suite à la fusion.", + "Data sources:": "Data sources:", "DataServer": "DataServer", "Date": "Date", "Date Committed": "Date de la promesse", @@ -830,6 +833,7 @@ "Email Address Type": "Type d’adresse e-mail", "Email Donation Services here": "Envoyer un e-mail aux services de don ici", "Email Newsletter List": "Liste du bulletin d’informations électronique", + "Email: ": "Email: ", "Employer": "Employeur", "Enable Calendar Integration": "Enable Calendar Integration", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Suivi", "Follow Up for Decision": "Suivi pour décision", "Follow-Up": "Suivi", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Montant étranger", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Type de numéro de téléphone", "Phone Numbers": "Numéros de téléphone", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Postal", "PHYSICAL": "Postal", "Physical Newsletter": "Bulletin d’information papier", diff --git a/public/locales/hy/translation.json b/public/locales/hy/translation.json index 23f2f3b9f..3cbfe08fe 100644 --- a/public/locales/hy/translation.json +++ b/public/locales/hy/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Հասցեն այլևս վավեր չէ", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Ադմին %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Վահանակ", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "«Պարտվողներ» բաժնի տվյալները կպատճենվեն «Հաղթող» բաժնում: Ստորև ընտրեք հաղոթողին: Միաձուլման գործընթացի ժամանակ ոչ մի տվյալչի կկորչի:", + "Data sources:": "Data sources:", "DataServer": "Տվյալների սերվեր", "Date": "Ամսաթիվ", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Հաղորդագրություն ուղարկել Նվիրատվությունների Ծառայության բաժնին այստեղ", "Email Newsletter List": "Email Newsletter List", + "Email: ": "Email: ", "Employer": "Գործատու", "Enable Calendar Integration": "Միացնել Google Օրացույցի ինտեգրացիան", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Հեռախոսահամարներ", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Փոստային առաքում տպված տարբերակով", "PHYSICAL": "Փոստային առաքում տպված տարբերակով", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/id/translation.json b/public/locales/id/translation.json index 3929f3a7e..4b8493196 100644 --- a/public/locales/id/translation.json +++ b/public/locales/id/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Alamat sudah tidak valid", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Dasbor", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Data dari \"orang yang kalah\" akan disalin ke \"pemenang\". Tidak ada data yang akan hilang setelah penggabungan.", + "Data sources:": "Data sources:", "DataServer": "Dataserver", "Date": "Tanggal", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Email Layanan Donasi di sini", "Email Newsletter List": "Daftar Buletin Email", + "Email: ": "Email: ", "Employer": "Pemberi kerja", "Enable Calendar Integration": "Aktifkan Integrasi Kalender", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Nomor Telepon", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Fisik", "PHYSICAL": "Fisik", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/it/translation.json b/public/locales/it/translation.json index eb764f624..dd76dd329 100644 --- a/public/locales/it/translation.json +++ b/public/locales/it/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Indirizzo non più valido", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Ammin. %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Pannello di controllo", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "I dati dei \"perdenti\" verranno copiati in quelli dei \"vincenti\". Seleziona il campione vincente qui sotto. La consolidazione non ti farà perdere nessun dato.", + "Data sources:": "Data sources:", "DataServer": "Dataserver", "Date": "Data", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Invia e-mail ai servizi di donazione qui", "Email Newsletter List": "Lista di newsletter e-mail", + "Email: ": "Email: ", "Employer": "Datore di lavoro", "Enable Calendar Integration": "Attiva l'integrazione del calendario", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Numeri di telefono", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Cartacea", "PHYSICAL": "Cartacea", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/ko/translation.json b/public/locales/ko/translation.json index e74bd2daa..bcb561692 100644 --- a/public/locales/ko/translation.json +++ b/public/locales/ko/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "더 이상 유효하지 않은 주소입니다.", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "관리자 %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "대시보드", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "\"losers\"의 데이터가 \"winner\"로 복사됩니다. 아래에서 winner를 선택하십시오. 머징으로 데이터가 손실되지는 않습니다.", + "Data sources:": "Data sources:", "DataServer": "데이터서버", "Date": "날짜", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "여기에서 기부 서비스 이메일", "Email Newsletter List": "뉴스레터 리스트로 이메일 발송", + "Email: ": "Email: ", "Employer": "고용인", "Enable Calendar Integration": "캘린더 통합 켜기", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "전화번호", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "물리적", "PHYSICAL": "물리적", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/my/translation.json b/public/locales/my/translation.json index f80c97c2c..30ca4cfdb 100644 --- a/public/locales/my/translation.json +++ b/public/locales/my/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "မနေတော့သောလိပ်စာ", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Dashboard", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.", + "Data sources:": "Data sources:", "DataServer": "Dataserver", "Date": "Date", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "ဤနေရာတွင်အီးမေးလ်လှူဒါန်းခြင်းoန်ဆောင်မှုများ", "Email Newsletter List": "Email Newsletter List", + "Email: ": "Email: ", "Employer": "အလုပ်ရှင်", "Enable Calendar Integration": "Enable-ပြက္ခဒိန်ပေါင်းစည်းရေး", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "နောက်ဆက်တွဲ", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Phone Numbers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Physical", "PHYSICAL": "Physical", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/nl-NL/translation.json b/public/locales/nl-NL/translation.json index 82055ec6c..c048afb2c 100644 --- a/public/locales/nl-NL/translation.json +++ b/public/locales/nl-NL/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Adres niet langer geldig", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Beheerder %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Dashboard", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Gegevens van de \"verliezers\" worden gekopieerd naar de \"winnaar\". Selecteer hieronder de winnaar. Er gaan geen gegevens verloren door samen te voegen.", + "Data sources:": "Data sources:", "DataServer": "DataServer", "Date": "Datum", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "E-mail de financiële afdeling van jouw organisatie hier", "Email Newsletter List": "E-Mail Nieuwsbrief Lijst", + "Email: ": "Email: ", "Employer": "Werkgever", "Enable Calendar Integration": "Agenda Integratie Inschakelen", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Telefoon Nummers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Papier", "PHYSICAL": "Papier", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/pl/translation.json b/public/locales/pl/translation.json index 413286f79..e4fd52617 100644 --- a/public/locales/pl/translation.json +++ b/public/locales/pl/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Adres już niepoprawny", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Kokpit", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Dane z \"przegranych\" zostaną skopiowane do \"zwycięzcy\". Wybierz zwycięzcę poniżej. Żadne dane nie zostaną utracone przez połączenie.", + "Data sources:": "Data sources:", "DataServer": "Serwer danych", "Date": "Data", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Wyślij e-mail do Serwisu Darowizn", "Email Newsletter List": "Email Newsletter List", + "Email: ": "Email: ", "Employer": "Pracodawca", "Enable Calendar Integration": "Włącz integrację kalendarza", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Numery telefonów", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Fizyczny", "PHYSICAL": "Fizyczny", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/pt-BR/translation.json b/public/locales/pt-BR/translation.json index 6ad2b27c0..b4740fad4 100644 --- a/public/locales/pt-BR/translation.json +++ b/public/locales/pt-BR/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Endereço atualmente inválido", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Administrador %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Painel", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Os dados dos \"perdedores\" serão copiados para o \"vencedor\". Selecione o vencedor abaixo. Nenhum dado será perdido na mesclagem.", + "Data sources:": "Data sources:", "DataServer": "Servidor de dados", "Date": "Data", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Envie e-mail para o Serviço de Donativos aqui", "Email Newsletter List": "Lista de Emails da Carta de Oração", + "Email: ": "Email: ", "Employer": "Empregador", "Enable Calendar Integration": "Ativar Integração da Agenda", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Números telefônicos", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Físico", "PHYSICAL": "Físico", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/ro/translation.json b/public/locales/ro/translation.json index 0d42df367..094ecb82e 100644 --- a/public/locales/ro/translation.json +++ b/public/locales/ro/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Address no longer valid", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Bordul de Comandă", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Informația din ”Eșecuri” va fi copiată la ”câștiguri”. Alege câștigătorul de mai jos. Nici o informație nu va fi pierdută prin unirea conturilor.", + "Data sources:": "Data sources:", "DataServer": "DataServer", "Date": "Data", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Trimite email la Donation Services aici", "Email Newsletter List": "Lista Email Newsletter", + "Email: ": "Email: ", "Employer": "Angajator", "Enable Calendar Integration": "Permite Integrarea Calendarului", "Enabled Google Calendar Integration!": "Permite Integrarea Calendarului", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Suma în valuta străină", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "NUmere de Telefon", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Fizic", "PHYSICAL": "Fizic", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index d399b2a13..dcbc20513 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Адрес успешно удален", "Address no longer valid": "Адрес больше недействителен", "Address updated successfully": "Адрес успешно обновлен", + "Address: ": "Address: ", "Addresses": "Адреса", "Addresses will be formatted based on country. (Experimental)": "Адреса будут отформатированы по странам. (Экспериментальная функция)", "Admin %": "Администратор %", @@ -482,6 +483,7 @@ "Bangladesh": "Бангладеш", "Barbados": "Барбадос", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Приложение {{appName}} может анализировать историю пожертвований партнера и присылать уведомления о наиболее важных событиях. Алгоритм обнаружения основан на наборе правил, которые, в основном, работают верно. Прежде чем связаться с партнером, проверьте данное событие вручную.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Беларусь", "Belgium": "Бельгия", "Belize": "Белиз", @@ -710,6 +712,7 @@ "Czech Republic": "Чешская Республика", "Dashboard": "Панель управления", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Данные «проигравших» будут скопированы в «победителя». Выберите «победителя» ниже. Во время объединения данные не будут потеряны.", + "Data sources:": "Data sources:", "DataServer": "Сервер данных", "Date": "Дата", "Date Committed": "Дата пожертвования", @@ -830,6 +833,7 @@ "Email Address Type": "Тип адреса эл. почты", "Email Donation Services here": "Напишите в отдел пожертвований здесь", "Email Newsletter List": "Список эл. адресов для новостной рассылки", + "Email: ": "Email: ", "Employer": "Работодатель", "Enable Calendar Integration": "Включить интеграцию календаря", "Enabled Google Calendar Integration!": "Включена интеграция с Google Календарем!", @@ -946,6 +950,7 @@ "Follow up": "Повторный контакт", "Follow Up for Decision": "Повторный контакт для получения решения", "Follow-Up": "Повторный контакт", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "Приложение {{appName}} может уведомлять вас о каждом событии по эл. почте и создавать для них задания- напоминания. Опции из списка ниже позволят контролировать ситуацию.", "Foreign Amount": "Сумма в иностранной валюте", "Fourteen month report table": "Таблица отчета за четырнадцатый месяц", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Тип номера телефона", "Phone Numbers": "Номера телефона", "Phone numbers updated!": "Телефонные номера обновлены!", + "Phone: ": "Phone: ", "Physical": "Офлайн", "PHYSICAL": "Псьмо по почте", "Physical Newsletter": "Физическая рассылка", diff --git a/public/locales/th/translation.json b/public/locales/th/translation.json index ddebc0fa9..5cfc1abed 100644 --- a/public/locales/th/translation.json +++ b/public/locales/th/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "ที่อยู่นี้ใช้ไม่ได้อีก", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "ผู้ดูแลระบบ %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "แดชบอร์ด", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "ระบบจะคัดลอกข้อมูล \"ผู้แพ้\" ไปยัง \"ผู้ชนะ\" โปรดเลือกผู้ชนะด้านล่าง การรวมกันนี้จะไม่ทำให้ข้อมูลสูญหาย", + "Data sources:": "Data sources:", "DataServer": "เซิร์ฟเวอร์ข้อมูล", "Date": "วันที่", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "อีเมลหน่วยการกุศลที่นี่", "Email Newsletter List": "รายชื่อจดหมายข่าวทางอีเมล", + "Email: ": "Email: ", "Employer": "นายจ้าง", "Enable Calendar Integration": "เปิดใช้การรวมปฏิทิน", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "เบอร์โทร", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "ฉบับตีพิมพ์", "PHYSICAL": "ฉบับตีพิมพ์", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/tr/translation.json b/public/locales/tr/translation.json index 7685eb31f..d27d7c883 100644 --- a/public/locales/tr/translation.json +++ b/public/locales/tr/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Adres başarıyla silindi", "Address no longer valid": "Adres artık geçerli değil", "Address updated successfully": "Adres başarıyla güncellendi", + "Address: ": "Address: ", "Addresses": "Adresler", "Addresses will be formatted based on country. (Experimental)": "Adres formatları ülkeye göre biçimlendirilecektir (Deneysel)", "Admin %": "Yönetici %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladeş", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Bir ortağın bağış geçmişinin bir analizine bağlı olarak, {{appName}} sizi muhtemelen takip etmek isteyeceğiniz etkinliklerden haberdar edebilir. Algılama mantığı, çoğu zaman doğru olan bir dizi kurala dayalıdır, ancak gene de ortakla iletişim kurmadan önce bir etkinliği bizzat doğrulamak istemeniz iyi olur.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belçika", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Çek Cumhuriyeti", "Dashboard": "Panel", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "\"Kaybedenler\"den gelen veriler \"kazanan\"a kopyalanacak. Aşağıda kazananı seçin. Birleştirme yoluyla hiç veri kaybedilmeyecek.", + "Data sources:": "Data sources:", "DataServer": "Dataserver", "Date": "Tarih", "Date Committed": "Taahhüt Tarihi", @@ -830,6 +833,7 @@ "Email Address Type": "E-posta Adresi Türü", "Email Donation Services here": "Bağış Hizmetlerine Buradan E-posta Gönder", "Email Newsletter List": "E-posta Haber Bülteni Listesi", + "Email: ": "Email: ", "Employer": "İşveren", "Enable Calendar Integration": "Takvim Entegrasyonunu Etkinleştir", "Enabled Google Calendar Integration!": "Google Takvim Entegrasyonu Etkinleştirildi!", @@ -946,6 +950,7 @@ "Follow up": "Takip Et", "Follow Up for Decision": "Karar Takibi", "Follow-Up": "Takip", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "Her etkinlik için, {{appName}} sizi e-posta aracılığıyla haberdar edebilir ve ayrıca size etkinlikle ilgili bir şey yapmanızı hatırlatan bir görev girişi oluşturabilir. Aşağıdaki seçenekler o davranışı kontrol etmenize olanak sağlar.", "Foreign Amount": "Yabancı Tutar", "Fourteen month report table": "On dört aylık rapor tablosu", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Telefon Numarası Türü", "Phone Numbers": "Telefon Numaraları", "Phone numbers updated!": "Telefon numaraları güncellendi!", + "Phone: ": "Phone: ", "Physical": "Fiziki", "PHYSICAL": "Fiziki", "Physical Newsletter": "Fiziksel Haber Bülteni", diff --git a/public/locales/uk/translation.json b/public/locales/uk/translation.json index a783584e3..9f41646c5 100644 --- a/public/locales/uk/translation.json +++ b/public/locales/uk/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Address no longer valid", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "Dashboard", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.", + "Data sources:": "Data sources:", "DataServer": "DataServer", "Date": "Date", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Email Donation Services here", "Email Newsletter List": "Email Newsletter List", + "Email: ": "Email: ", "Employer": "Employer", "Enable Calendar Integration": "Enable Calendar Integration", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Phone Numbers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Physical", "PHYSICAL": "Physical", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/vi/translation.json b/public/locales/vi/translation.json index 818add314..50b271773 100644 --- a/public/locales/vi/translation.json +++ b/public/locales/vi/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "Địa chỉ không còn hiệu lực", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "Admin %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "BẢNG ĐIỀU KHIỂN", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "Dữ liệu từ \"người thất bại\" sẽ sao chép đến người thắng. Chọn người thắng bên dưới. Không dữ liệu nào bị mất bởi việc trộn này.", + "Data sources:": "Data sources:", "DataServer": "Dữ liệu máy chủ", "Date": "Ngày", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "Email Address Type", "Email Donation Services here": "Dịch Vụ Email Dâng Hiến ở Đây", "Email Newsletter List": "Email Newsletter List", + "Email: ": "Email: ", "Employer": "Nhân viên", "Enable Calendar Integration": "Cho Phép Hợp Nhất Lịch", "Enabled Google Calendar Integration!": "Enabled Google Calendar Integration!", @@ -946,6 +950,7 @@ "Follow up": "Chăm sóc", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Phone Numbers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Vật lý", "PHYSICAL": "Vật lý", "Physical Newsletter": "Physical Newsletter", diff --git a/public/locales/zh-Hans-CN/translation.json b/public/locales/zh-Hans-CN/translation.json index 8bb8d6da3..531ee04d2 100644 --- a/public/locales/zh-Hans-CN/translation.json +++ b/public/locales/zh-Hans-CN/translation.json @@ -335,6 +335,7 @@ "Address deleted successfully": "Address deleted successfully", "Address no longer valid": "地址不再有效", "Address updated successfully": "Address updated successfully", + "Address: ": "Address: ", "Addresses": "Addresses", "Addresses will be formatted based on country. (Experimental)": "Addresses will be formatted based on country. (Experimental)", "Admin %": "行政 %", @@ -482,6 +483,7 @@ "Bangladesh": "Bangladesh", "Barbados": "Barbados", "Based on an analysis of a partner's giving history, {{appName}} can\n notify you of events that you will probably want to follow up on. The\n detection logic is based on a set of rules that are right most of the\n time, but you will still want to verify an event manually before\n contacting the partner.": "根据对合作伙伴奉献历史的分析,{{appName}} 可以通知你 那些你可能想要跟进的事件。 检测的逻辑 是基于一组大部分时候都正确的规则,但你可能仍需要在联系合作伙伴之前人工确认这些事件。", + "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.": "Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.", "Belarus": "Belarus", "Belgium": "Belgium", "Belize": "Belize", @@ -710,6 +712,7 @@ "Czech Republic": "Czech Republic", "Dashboard": "显示板", "Data from the \"losers\" will get copied to the \"winner\". Select the winner below. No data will be lost by merging.": "来自“输家”的数据将被复制到“赢家”。 选择下面的赢家。 没有数据会因被合而丢失。", + "Data sources:": "Data sources:", "DataServer": "数据服务器", "Date": "日期", "Date Committed": "Date Committed", @@ -830,6 +833,7 @@ "Email Address Type": "电子邮件地址类型", "Email Donation Services here": "在这里发送奉献服务邮件", "Email Newsletter List": "电子邮件代祷信列表", + "Email: ": "Email: ", "Employer": "雇主", "Enable Calendar Integration": "启用日历整合", "Enabled Google Calendar Integration!": "启用 Google 日历集成!", @@ -946,6 +950,7 @@ "Follow up": "Follow up", "Follow Up for Decision": "Follow Up for Decision", "Follow-Up": "Follow-Up", + "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.": "For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.", "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.": "For each event {{appName}} can notify you via email and also create a task\n entry reminding you to do something about it. The options below allow\n you to control that behavior.", "Foreign Amount": "Foreign Amount", "Fourteen month report table": "Fourteen month report table", @@ -1552,6 +1557,7 @@ "Phone Number Type": "Phone Number Type", "Phone Numbers": "Phone Numbers", "Phone numbers updated!": "Phone numbers updated!", + "Phone: ": "Phone: ", "Physical": "Physical", "PHYSICAL": "纸质的", "Physical Newsletter": "Physical Newsletter", diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactDetailsMoreActions/ContactDetailsMoreActions.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactDetailsMoreActions/ContactDetailsMoreActions.tsx index 42926ed15..e2bb9ea30 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactDetailsMoreActions/ContactDetailsMoreActions.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactDetailsMoreActions/ContactDetailsMoreActions.tsx @@ -296,6 +296,7 @@ export const ContactDetailsMoreAcitions: React.FC< setOpen={setDeleteModalOpen} deleting={deleting} deleteContact={handleDeleteContact} + contactId={contactId} /> )} {openHideModal && ( diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/ContactSource.graphql b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/ContactSource.graphql new file mode 100644 index 000000000..83dbaa392 --- /dev/null +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/ContactSource.graphql @@ -0,0 +1,29 @@ +query ContactSource($accountListId: ID!, $contactId: ID!) { + contact(accountListId: $accountListId, id: $contactId) { + id + source + addresses { + nodes { + id + source + } + } + people { + nodes { + id + emailAddresses { + nodes { + id + source + } + } + phoneNumbers { + nodes { + id + source + } + } + } + } + } +} diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.test.tsx new file mode 100644 index 000000000..4837e518d --- /dev/null +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.test.tsx @@ -0,0 +1,219 @@ +import React from 'react'; +import { ThemeProvider } from '@mui/material/styles'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon'; +import { render, waitFor } from '@testing-library/react'; +import { SnackbarProvider } from 'notistack'; +import TestRouter from '__tests__/util/TestRouter'; +import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; +import { ContactSourceEnum } from 'src/graphql/types.generated'; +import theme from 'src/theme'; +import { ContactSourceQuery } from './ContactSource.generated'; +import { DeleteContactModal } from './DeleteContactModal'; + +const contactId = 'contact-id'; +const mutationSpy = jest.fn(); +const setOpen = jest.fn(); +const deleteContact = jest.fn(); + +interface TestComponentProps { + open?: boolean; + deleting?: boolean; + contactSource?: ContactSourceEnum; + addressSources?: string[]; + emailSources?: string[]; + phoneSources?: string[]; +} + +const TestComponent: React.FC = ({ + open = true, + deleting = false, + contactSource = ContactSourceEnum.Mpdx, + addressSources = [], + emailSources = [], + phoneSources = [], +}) => ( + + + + + + mocks={{ + ContactSource: { + contact: { + id: contactId, + source: contactSource, + addresses: { + nodes: addressSources.map((source) => ({ source })), + }, + people: { + nodes: [ + { + emailAddresses: { + nodes: emailSources.map((source) => ({ source })), + }, + phoneNumbers: { + nodes: phoneSources.map((source) => ({ source })), + }, + }, + { + emailAddresses: { + nodes: emailSources.map((source) => ({ source })), + }, + phoneNumbers: { + nodes: phoneSources.map((source) => ({ source })), + }, + }, + ], + }, + }, + }, + }} + onCall={mutationSpy} + > + + + + + + +); + +describe('DeleteContactModal', () => { + it('should not show modal if not open', () => { + const { queryByText } = render(); + + expect( + queryByText(/Are you sure you want to permanently delete this contact?/), + ).not.toBeInTheDocument(); + }); + + it('should be able to delete contact', async () => { + const { getByText, getByRole } = render(); + + await waitFor(() => { + expect(mutationSpy).toHaveGraphqlOperation('ContactSource'); + }); + + expect( + getByText(/Are you sure you want to permanently delete this contact?/), + ).toBeInTheDocument(); + + expect(getByRole('button', { name: 'delete contact' })).toBeInTheDocument(); + }); + + it('should prevent user from deleting contact while currently deleting contact', async () => { + const { getByRole } = render(); + + await waitFor(() => { + expect(mutationSpy).toHaveGraphqlOperation('ContactSource'); + }); + + expect(getByRole('button', { name: 'delete contact' })).toBeDisabled(); + }); + + describe('Show third party message', () => { + interface TestProps { + testName: string; + props: TestComponentProps; + } + const tests: TestProps[] = [ + { + testName: 'disables deletion if contact created by third party', + props: { contactSource: ContactSourceEnum.GiveSite }, + }, + { + testName: + "disables deletion if a contact's address is sourced by a third party", + props: { + addressSources: ['Siebel', 'MPDX'], + }, + }, + { + testName: + "disables deletion if a contact's phone or email is sourced by a third party", + props: { + emailSources: ['Siebel', 'MPDX'], + phoneSources: ['Siebel', 'MPDX'], + }, + }, + { + testName: + "disables deletion if only one contact's phone number is sourced by a third party", + props: { + emailSources: ['MPDX', 'MPDX'], + phoneSources: ['MPDX', 'Siebel'], + }, + }, + ]; + + test.each(tests)('$testName', async ({ props }) => { + const { findByText, getByRole } = render(); + + expect( + await findByText( + /its data may sync with Donation Services or other third-party systems/, + ), + ).toBeInTheDocument(); + + expect( + getByRole('button', { name: 'delete contact' }), + ).toBeInTheDocument(); + }); + + it('should show third party source for contact', async () => { + const { findByText } = render( + , + ); + + expect(await findByText('Contact: GIVE_SITE')).toBeInTheDocument(); + expect( + await findByText('Address: US Donation Services'), + ).toBeInTheDocument(); + expect( + await findByText('Email: US Donation Services'), + ).toBeInTheDocument(); + expect( + await findByText('Phone: US Donation Services'), + ).toBeInTheDocument(); + }); + }); + + describe('Show normal delete message', () => { + it('should show modal and be able to delete user', async () => { + const { getByText, getByRole } = render( + , + ); + + await waitFor(() => { + expect(mutationSpy).toHaveGraphqlOperation('ContactSource'); + }); + + expect( + getByText(/Are you sure you want to permanently delete this contact?/), + ).toBeInTheDocument(); + + expect( + getByRole('button', { name: 'delete contact' }), + ).toBeInTheDocument(); + }); + }); +}); diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx index 92a2aabac..d6c2bdba5 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/DeleteContactModal/DeleteContactModal.tsx @@ -1,9 +1,14 @@ -import React from 'react'; +import React, { useMemo } from 'react'; +import { Email, Person, Phone, Place } from '@mui/icons-material'; import { CircularProgress, DialogActions, DialogContent, - DialogContentText, + List, + ListItem, + ListItemIcon, + ListItemText, + Typography, } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; @@ -12,16 +17,27 @@ import { DeleteButton, } from 'src/components/common/Modal/ActionButtons/ActionButtons'; import Modal from 'src/components/common/Modal/Modal'; +import { useAccountListId } from 'src/hooks/useAccountListId'; +import { isEditableSource, sourceToStr } from 'src/utils/sourceHelper'; +import { useContactSourceQuery } from './ContactSource.generated'; const LoadingIndicator = styled(CircularProgress)(({ theme }) => ({ margin: theme.spacing(0, 1, 0, 0), })); +interface DataInfo { + canDeleteWithoutIssues: boolean; + contactSource: string; + addressSources: string[]; + emailSources: string[]; + phoneSources: string[]; +} interface DeleteContactModalProps { open: boolean; setOpen: (open: boolean) => void; deleting: boolean; deleteContact: () => void; + contactId: string; } export const DeleteContactModal: React.FC = ({ @@ -29,8 +45,71 @@ export const DeleteContactModal: React.FC = ({ setOpen, deleting, deleteContact, + contactId, }) => { const { t } = useTranslation(); + const accountListId = useAccountListId() ?? ''; + + const { data } = useContactSourceQuery({ + variables: { accountListId, contactId }, + skip: !open && !contactId, + }); + const contactSources = data?.contact; + + const dataInfo: DataInfo = useMemo(() => { + if (!contactSources) { + return { + canDeleteWithoutIssues: true, + contactSource: sourceToStr(t, 'MPDX'), + addressSources: [], + emailSources: [], + phoneSources: [], + }; + } + + // We ensure the contact was created on MPDX and that all the data is editable. + // If any data is not editable, this means it was created by a third party. + // Which will only recreate the data after deleting it on MPDX. + // To prevent this confusion, we do not allow a contact to be deleted if it has non editable data. + + const addressSources = new Set(); + const emailSources = new Set(); + const phoneSources = new Set(); + + contactSources.addresses?.nodes.forEach((address) => { + if (!isEditableSource(address.source)) { + addressSources.add(address.source); + } + }); + + contactSources.people?.nodes.forEach((person) => { + person.emailAddresses.nodes.forEach((email) => { + if (!isEditableSource(email.source)) { + emailSources.add(email.source); + } + }); + person.phoneNumbers.nodes.forEach((phone) => { + if (!isEditableSource(phone.source)) { + phoneSources.add(phone.source); + } + }); + }); + + return { + canDeleteWithoutIssues: + isEditableSource(contactSources.source ?? undefined) && + !addressSources.size && + !emailSources.size && + !phoneSources.size, + contactSource: sourceToStr(t, contactSources.source), + addressSources: [...addressSources].map((source) => + sourceToStr(t, source), + ), + emailSources: [...emailSources].map((source) => sourceToStr(t, source)), + phoneSources: [...phoneSources].map((source) => sourceToStr(t, source)), + }; + }, [contactSources]); + return ( = ({ handleClose={() => setOpen(false)} > - - {t( - 'Are you sure you want to permanently delete this contact? Doing so will permanently delete this contacts information, as well as task history. This cannot be undone. If you wish to keep this information, you can try hiding this contact instead.', - )} - + {dataInfo.canDeleteWithoutIssues ? ( + + {t( + `Are you sure you want to permanently delete this contact? Doing so will permanently delete this contacts information, as well as task history. This cannot be undone. If you wish to keep this information, you can try hiding this contact instead.`, + )} + + ) : ( + <> + + {t( + `Be cautious when deleting this contact, as its data may sync with Donation Services or other third-party systems. Deleting the contact will not remove it from those systems; consider hiding the contact instead.`, + )} + + + {t( + `For contacts originating from Donation Services or DonorHub, email Donation Services to request deletion.`, + )} + +
+
+ {t('Data sources:')} + + {!!dataInfo.contactSource && ( + + + + + + + )} + + {!!dataInfo.addressSources.length && ( + + + + + + + )} + {!!dataInfo.emailSources.length && ( + + + + + + + )} + {!!dataInfo.phoneSources.length && ( + + + + + + + )} + + + )}
({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ...jest.requireActual('notistack'), + useSnackbar: () => { + return { + enqueueSnackbar: mockEnqueue, + }; + }, +})); + const handleClose = jest.fn(); +const mockEnqueue = jest.fn(); +const mutationSpy = jest.fn(); + const contactMock = gqlMock( ContactDonorAccountsFragmentDoc, { @@ -98,85 +113,44 @@ const newContactMock = gqlMock( }, ); -jest.mock('next/router', () => ({ - useRouter: () => { - return { - query: { accountListId: 'abc' }, - isReady: true, - }; - }, -})); +interface ComponentsProps { + isNewContact?: boolean; +} -const mockEnqueue = jest.fn(); - -jest.mock('notistack', () => ({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ...jest.requireActual('notistack'), - useSnackbar: () => { - return { - enqueueSnackbar: mockEnqueue, - }; - }, -})); +const Components = ({ isNewContact = false }: ComponentsProps) => ( + + + + + + + + + + + +); describe('EditPartnershipInfoModal', () => { it('should render edit partnership info modal', async () => { - const { getByText } = render( - - - - - - - - - , - ); - await waitFor(() => - expect(getByText('Edit Partnership')).toBeInTheDocument(), - ); + const { findByText } = render(); + + expect(await findByText('Edit Partnership')).toBeInTheDocument(); }); - it('should handle closing modal | Close Button', async () => { - const { getByText, getByLabelText } = render( - - - - - - - - - , - ); + it('should handle closing modal | Close Button', () => { + const { getByText, getByLabelText } = render(); expect(getByText('Edit Partnership')).toBeInTheDocument(); userEvent.click(getByLabelText('Close')); expect(handleClose).toHaveBeenCalled(); }); - it('should handle closing modal | Cancel Button', async () => { - const { getByText } = render( - - - - - - - - - , - ); + it('should handle closing modal | Cancel Button', () => { + const { getByText } = render(); expect(getByText('Edit Partnership')).toBeInTheDocument(); userEvent.click(getByText('Cancel')); @@ -185,18 +159,7 @@ describe('EditPartnershipInfoModal', () => { it('should save when only status is inputted', async () => { const { getByText, getByRole, findByRole } = render( - - - - - - - - - , + , ); const statusInput = getByRole('combobox', { name: 'Status' }); @@ -219,20 +182,7 @@ describe('EditPartnershipInfoModal', () => { it('should handle editing status | Non-Financial', async () => { const { getByLabelText, getByText, getByRole, getByTestId, queryByText } = - render( - - - - - - - - - , - ); + render(); const statusInput = getByLabelText('Status'); const amountInput = getByLabelText('Amount'); const frequencyInput = getByRole('combobox', { name: 'Frequency' }); @@ -288,20 +238,7 @@ describe('EditPartnershipInfoModal', () => { findByText, getByTestId, queryByTestId, - } = render( - - - - - - - - - , - ); + } = render(); const statusInput = getByLabelText('Status'); const amountInput = getByLabelText('Amount'); const frequencyInput = getByRole('combobox', { name: 'Frequency' }); @@ -352,20 +289,7 @@ describe('EditPartnershipInfoModal', () => { }); it('should handle editing status | Financial', async () => { - const { getByLabelText, getByText } = render( - - - - - - - - - , - ); + const { getByLabelText, getByText } = render(); const statusInput = getByLabelText('Status'); const amountInput = getByLabelText('Amount'); const frequencyInput = getByLabelText('Frequency'); @@ -397,20 +321,7 @@ describe('EditPartnershipInfoModal', () => { }); it('should handle editing commitment received', async () => { - const { getByLabelText, getByText } = render( - - - - - - - - - , - ); + const { getByLabelText, getByText } = render(); const commitmentReceivedInput = getByLabelText('Commitment Received'); expect(commitmentReceivedInput).not.toBeChecked(); @@ -432,20 +343,7 @@ describe('EditPartnershipInfoModal', () => { }); it('should handle editing send appeals', async () => { - const { getByLabelText, getByText } = render( - - - - - - - - - , - ); + const { getByLabelText, getByText } = render(); const sendAppealsInput = getByLabelText('Send Appeals'); expect(sendAppealsInput).not.toBeChecked(); @@ -467,22 +365,9 @@ describe('EditPartnershipInfoModal', () => { }); it('should handle editing currency', async () => { - const { getByLabelText, getByText } = render( - - - - - - - - - , - ); - await waitFor(() => expect(getByLabelText('Currency')).toBeInTheDocument()); - const currencyInput = getByLabelText('Currency'); + const { findByLabelText, getByText } = render(); + + const currencyInput = await findByLabelText('Currency'); userEvent.click(currencyInput); userEvent.click(getByText('Congolese Franc - CDF (CDF)')); @@ -499,22 +384,7 @@ describe('EditPartnershipInfoModal', () => { }); it('should handle editing Likely to give', async () => { - const mutationSpy = jest.fn(); - const { getByRole } = render( - - - - - - - - - , - ); - mutationSpy.mockClear(); + const { getByRole } = render(); userEvent.click(getByRole('combobox', { name: 'Likely To Give' })); userEvent.click(getByRole('option', { name: 'Most Likely' })); @@ -528,42 +398,24 @@ describe('EditPartnershipInfoModal', () => { }, ), ); - expect(mutationSpy.mock.lastCall).toMatchObject([ - { - operation: { - operationName: 'UpdateContactPartnership', - variables: { - attributes: { - likelyToGive: 'MOST_LIKELY', - }, - }, - }, + + expect(mutationSpy).toHaveGraphqlOperation('UpdateContactPartnership', { + attributes: { + likelyToGive: 'MOST_LIKELY', }, - ]); + }); }); it('should handle editing start date', async () => { - const { getByLabelText, getByText, getAllByText } = render( - - - - - - - - - , + const { getByLabelText, getByText, findByText, getAllByText } = render( + , ); const datePickerButton = getByLabelText('Start Date'); userEvent.click(datePickerButton); const day = await waitFor(async () => getAllByText('30')[0]); userEvent.click(day); - const okayButton = await waitFor(async () => getByText('OK')); - userEvent.click(okayButton); + userEvent.click(await findByText('OK')); userEvent.click(getByText('Save')); await waitFor(() => expect(mockEnqueue).toHaveBeenCalledWith( @@ -576,23 +428,24 @@ describe('EditPartnershipInfoModal', () => { expect(handleClose).toHaveBeenCalled(); }); - it('should handle editing newsletter', async () => { - const mutationSpy = jest.fn(); - const { getByRole } = render( - - - - - - - - - , + it('should allow for user to remove the start date', async () => { + const { getByLabelText, getByText, findByText } = render(); + const datePickerButton = getByLabelText('Start Date'); + userEvent.click(datePickerButton); + userEvent.click(await findByText('Clear')); + userEvent.click(getByText('Save')); + + await waitFor(() => + expect(mutationSpy).toHaveGraphqlOperation('UpdateContactPartnership', { + attributes: { + pledgeStartDate: null, + }, + }), ); - mutationSpy.mockClear(); + }); + + it('should handle editing newsletter', async () => { + const { getByRole } = render(); userEvent.click(getByRole('combobox', { name: 'Newsletter' })); userEvent.click(getByRole('option', { name: 'Physical' })); @@ -606,39 +459,17 @@ describe('EditPartnershipInfoModal', () => { }, ), ); - expect(mutationSpy.mock.lastCall).toMatchObject([ - { - operation: { - operationName: 'UpdateContactPartnership', - variables: { - attributes: { - sendNewsletter: 'PHYSICAL', - }, - }, - }, + expect(mutationSpy).toHaveGraphqlOperation('UpdateContactPartnership', { + attributes: { + sendNewsletter: 'PHYSICAL', }, - ]); + }); }); it('should handle editing contact name and primary contact', async () => { - const mutationSpy = jest.fn(); const newContactName = 'Guy, Cool and Neat'; const newPrimaryContactName = `${newContactMock.people.nodes[1].firstName} ${newContactMock.people.nodes[1].lastName}`; - const { getByRole } = render( - - - - - - - - - , - ); - mutationSpy.mockClear(); + const { getByRole } = render(); const contactTextBox = getByRole('textbox', { hidden: true, @@ -671,27 +502,15 @@ describe('EditPartnershipInfoModal', () => { }); it('should handle editing next ask date', async () => { - const { getByLabelText, getByText, getAllByText } = render( - - - - - - - - - , + const { getByLabelText, getByText, findByText, getAllByText } = render( + , ); const datePickerButton = getByLabelText('Next Increase Ask'); userEvent.click(datePickerButton); const day = await waitFor(async () => getAllByText('30')[0]); userEvent.click(day); - const okayButton = await waitFor(async () => getByText('OK')); - userEvent.click(okayButton); + userEvent.click(await findByText('OK')); userEvent.click(getByText('Save')); await waitFor(() => expect(mockEnqueue).toHaveBeenCalledWith( @@ -703,4 +522,20 @@ describe('EditPartnershipInfoModal', () => { ); expect(handleClose).toHaveBeenCalled(); }); + + it('should allow user to remove next ask date', async () => { + const { getByLabelText, getByText, findByText } = render(); + const datePickerButton = getByLabelText('Next Increase Ask'); + userEvent.click(datePickerButton); + userEvent.click(await findByText('Clear')); + userEvent.click(getByText('Save')); + + await waitFor(() => + expect(mutationSpy).toHaveGraphqlOperation('UpdateContactPartnership', { + attributes: { + nextAsk: null, + }, + }), + ); + }); }); diff --git a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx index 8af1ca3a1..7b40dc5ab 100644 --- a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx @@ -137,8 +137,8 @@ export const EditPartnershipInfoModal: React.FC< accountListId: accountListId ?? '', attributes: { ...attributes, - pledgeStartDate: attributes.pledgeStartDate?.toISODate(), - nextAsk: attributes.nextAsk?.toISODate(), + pledgeStartDate: attributes.pledgeStartDate?.toISODate() ?? null, + nextAsk: attributes.nextAsk?.toISODate() ?? null, primaryPersonId: attributes.primaryPersonId, }, }, diff --git a/src/components/Contacts/MassActions/RemoveTags/MassActionsRemoveTagsModal.tsx b/src/components/Contacts/MassActions/RemoveTags/MassActionsRemoveTagsModal.tsx index fa6583256..9b25e7f8e 100644 --- a/src/components/Contacts/MassActions/RemoveTags/MassActionsRemoveTagsModal.tsx +++ b/src/components/Contacts/MassActions/RemoveTags/MassActionsRemoveTagsModal.tsx @@ -34,6 +34,7 @@ interface MassActionsRemoveTagsModalProps { const ExistingTagButton = styled(Button)(() => ({ textTransform: 'none', width: 'fit-content', + justifyContent: 'left', })); const SelectedTagButton = styled(Button)(() => ({ @@ -107,7 +108,7 @@ export const MassActionsRemoveTagsModal: React.FC< const tagsData = contactsForTags?.contacts.nodes.map((contact) => contact.tagList) ?? []; - const contactsTagsList = [...new Set([...tagsData.flatMap((el) => el)])]; + const contactsTagsList = Array.from(new Set(tagsData.flat())).sort(); return ( diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index a0abbdf05..e0c89c10b 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -39,7 +39,7 @@ const Dashboard = ({ data, accountListId }: Props): ReactElement => { exit="exit" variants={variants} > - + ; +} + +const Components = ({ + healthIndicatorData = [], + monthlyGoalProps, +}: ComponentsProps) => ( + + mocks={{ + HealthIndicatorWidget: { + healthIndicatorData, + }, + }} + onCall={mutationSpy} + > + + +); describe('MonthlyGoal', () => { beforeEach(() => { @@ -9,9 +39,8 @@ describe('MonthlyGoal', () => { }); it('default', () => { - const { getByTestId, queryByTestId } = render( - , - ); + const { getByTestId, queryByTestId } = render(); + expect( queryByTestId('MonthlyGoalTypographyGoalMobile'), ).not.toBeInTheDocument(); @@ -44,7 +73,7 @@ describe('MonthlyGoal', () => { it('loading', () => { const { getByTestId } = render( - , + , ); expect( getByTestId('MonthlyGoalTypographyGoal').children[0].className, @@ -74,12 +103,8 @@ describe('MonthlyGoal', () => { it('props', () => { const { getByTestId, queryByTestId } = render( - , ); expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual( @@ -113,12 +138,13 @@ describe('MonthlyGoal', () => { it('props above goal', () => { const { getByTestId, queryByTestId } = render( - , ); expect( @@ -147,9 +173,7 @@ describe('MonthlyGoal', () => { }); it('default', () => { - const { getByTestId, queryByTestId } = render( - , - ); + const { getByTestId, queryByTestId } = render(); expect( getByTestId('MonthlyGoalTypographyGoalMobile').textContent, ).toEqual('$0'); @@ -166,12 +190,8 @@ describe('MonthlyGoal', () => { it('props', () => { const { getByTestId } = render( - , ); expect( @@ -179,4 +199,46 @@ describe('MonthlyGoal', () => { ).toEqual('€999.50'); }); }); + + describe('HealthIndicatorWidget', () => { + it('should not show the health indicator and keep Grid styles', async () => { + const { getByTestId, queryByText } = render( + , + ); + + await waitFor(() => { + expect(mutationSpy).toHaveGraphqlOperation('HealthIndicatorWidget'); + }); + expect(queryByText('MPD Health Indicator')).not.toBeInTheDocument(); + expect(getByTestId('containerGrid')).not.toHaveClass( + 'MuiGrid-spacing-xs-2', + ); + expect(getByTestId('goalGrid')).not.toHaveClass('MuiGrid-grid-xs-6'); + }); + + it('should show the health indicator and change Grid styles', async () => { + const { getByTestId, getByText } = render( + , + ); + + await waitFor(() => { + expect(mutationSpy).toHaveGraphqlOperation('HealthIndicatorWidget'); + }); + expect(getByText('MPD Health Indicator')).toBeInTheDocument(); + expect(getByTestId('containerGrid')).toHaveClass('MuiGrid-spacing-xs-2'); + expect(getByTestId('goalGrid')).toHaveClass('MuiGrid-grid-xs-6'); + }); + }); }); diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx index ffe4040c3..ac52790aa 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement } from 'react'; +import React, { ReactElement, useMemo, useState } from 'react'; import { Box, Button, @@ -7,10 +7,12 @@ import { Hidden, Skeleton, Theme, + Tooltip, Typography, } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; +import { HealthIndicatorWidget } from 'src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget'; import { ContactFilterPledgeReceivedEnum, StatusEnum, @@ -45,7 +47,7 @@ const useStyles = makeStyles()((_theme: Theme) => ({ }, })); -interface Props { +export interface MonthlyGoalProps { accountListId: string; loading?: boolean; goal?: number; @@ -63,15 +65,40 @@ const MonthlyGoal = ({ pledged = 0, totalGiftsNotStarted, currencyCode = 'USD', -}: Props): ReactElement => { - const { classes } = useStyles(); +}: MonthlyGoalProps): ReactElement => { const { t } = useTranslation(); + const { classes } = useStyles(); const locale = useLocale(); + const [showHealthIndicator, setShowHealthIndicator] = useState(false); + const [usingMachineCalculatedGoal, setUsingMachineCalculatedGoal] = + useState(false); + + const toolTipText = useMemo(() => { + const formattedGoal = currencyFormat(goal, currencyCode, locale); + return usingMachineCalculatedGoal + ? t( + 'Your current goal of {{goal}} is machine-calculated based on the past year of NetSuite data. You can adjust this goal in the settings preferences.', + { goal: formattedGoal }, + ) + : t( + 'Your current goal of {{goal}} is staff-entered, based on the value set in your settings preferences.', + { goal: formattedGoal }, + ); + }, [usingMachineCalculatedGoal, goal, currencyCode, locale]); + const receivedPercentage = received / goal; const pledgedPercentage = pledged / goal; const belowGoal = goal - pledged; const belowGoalPercentage = belowGoal / goal; + const cssProps = { + containerGrid: showHealthIndicator ? { spacing: 2 } : {}, + monthlyGoalGrid: showHealthIndicator + ? { xs: 12, md: 6, lg: 7 } + : { xs: 12 }, + statGrid: showHealthIndicator ? { xs: 6 } : { sm: 6, md: 3 }, + hIGrid: showHealthIndicator ? { xs: 12, md: 6, lg: 5 } : { xs: 0 }, + }; return ( <> @@ -103,146 +130,168 @@ const MonthlyGoal = ({ - - - - - - - -
- {t('Goal')} - - - {loading ? ( - - ) : ( - currencyFormat(goal, currencyCode, locale) - )} - - - - - -
- {t('Gifts Started')} - - - {loading ? ( - - ) : isNaN(receivedPercentage) ? ( - '-' - ) : ( - percentageFormat(receivedPercentage, locale) - )} - - - {loading ? ( - - ) : ( - currencyFormat(received, currencyCode, locale) - )} - - - - -
- {t('Commitments')} - - - {loading ? ( - - ) : isNaN(pledgedPercentage) ? ( - '-' - ) : ( - percentageFormat(pledgedPercentage, locale) - )} - - - {loading ? ( - - ) : ( - currencyFormat(pledged, currencyCode, locale) - )} - - - - {!isNaN(belowGoal) && belowGoal > 0 ? ( - + + + + + + + + + + + +
+ {t('Goal')} + + + {loading ? ( + + ) : ( + currencyFormat(goal, currencyCode, locale) + )} + + + + + + - {t('Below Goal')} +
+ {t('Gifts Started')} - {percentageFormat(belowGoalPercentage, locale)} + {loading ? ( + + ) : isNaN(receivedPercentage) ? ( + '-' + ) : ( + percentageFormat(receivedPercentage, locale) + )} - {currencyFormat(belowGoal, currencyCode, locale)} + {loading ? ( + + ) : ( + currencyFormat(received, currencyCode, locale) + )} - ) : ( - + - {t('Above Goal')} +
+ {t('Commitments')} {loading ? ( - ) : isNaN(belowGoalPercentage) ? ( + ) : isNaN(pledgedPercentage) ? ( '-' ) : ( - percentageFormat(-belowGoalPercentage, locale) + percentageFormat(pledgedPercentage, locale) )} {loading ? ( ) : ( - currencyFormat(-belowGoal, currencyCode, locale) + currencyFormat(pledged, currencyCode, locale) )} - )} - - - - + + {!isNaN(belowGoal) && belowGoal > 0 ? ( + + + {t('Below Goal')} + + + {percentageFormat(belowGoalPercentage, locale)} + + + {currencyFormat(belowGoal, currencyCode, locale)} + + + ) : ( + + + {t('Above Goal')} + + + {loading ? ( + + ) : isNaN(belowGoalPercentage) ? ( + '-' + ) : ( + percentageFormat(-belowGoalPercentage, locale) + )} + + + {loading ? ( + + ) : ( + currencyFormat(-belowGoal, currencyCode, locale) + )} + + + )} + + + + + + + + + + ); }; diff --git a/src/components/Reports/FourteenMonthReports/FourteenMonthReport.tsx b/src/components/Reports/FourteenMonthReports/FourteenMonthReport.tsx index 74a877d8e..10009ec8a 100644 --- a/src/components/Reports/FourteenMonthReports/FourteenMonthReport.tsx +++ b/src/components/Reports/FourteenMonthReports/FourteenMonthReport.tsx @@ -69,6 +69,7 @@ export const FourteenMonthReport: React.FC = ({ useEffect(() => { (async () => { try { + setFourteenMonthReportError(''); const designationAccountFilter = designationAccounts?.length ? `&filter[designation_account_id]=${designationAccounts.join(',')}` : ''; @@ -101,7 +102,7 @@ export const FourteenMonthReport: React.FC = ({ } } })(); - }, [designationAccounts, currencyType]); + }, [accountListId, designationAccounts, currencyType]); // Generate a table for each currency group in the report const currencyTables = useMemo( diff --git a/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.graphql b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.graphql new file mode 100644 index 000000000..3562f0227 --- /dev/null +++ b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.graphql @@ -0,0 +1,15 @@ +query HealthIndicatorWidget($accountListId: ID!, $month: ISO8601Date!) { + healthIndicatorData( + accountListId: $accountListId + beginDate: $month + endDate: $month + ) { + id + overallHi + ownershipHi + consistencyHi + successHi + depthHi + machineCalculatedGoal + } +} diff --git a/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.test.tsx b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.test.tsx new file mode 100644 index 000000000..a61c85ccd --- /dev/null +++ b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.test.tsx @@ -0,0 +1,135 @@ +import { render, waitFor } from '@testing-library/react'; +import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; +import { HealthIndicatorWidget } from './HealthIndicatorWidget'; +import { HealthIndicatorWidgetQuery } from './HealthIndicatorWidget.generated'; + +const accountListId = 'account-list-1'; +const setShowHealthIndicator = jest.fn(); +const setUsingMachineCalculatedGoal = jest.fn(); +const mutationSpy = jest.fn(); + +const healthIndicatorScore = { + id: '1', + overallHi: 90, + ownershipHi: 80, + consistencyHi: 70, + successHi: 60, + depthHi: 50, + machineCalculatedGoal: 7000, +}; + +interface ComponentsProps { + healthIndicatorData?: HealthIndicatorWidgetQuery['healthIndicatorData']; + showHealthIndicator?: boolean; + goal?: number; +} +const Components = ({ + healthIndicatorData = [], + showHealthIndicator = true, + goal = 7000, +}: ComponentsProps) => ( + + mocks={{ + HealthIndicatorWidget: { + healthIndicatorData, + }, + }} + onCall={mutationSpy} + > + + +); + +describe('HealthIndicatorWidget', () => { + it('renders nothing when there is no data', async () => { + const { queryByText, container } = render( + , + ); + + await waitFor(() => { + expect(mutationSpy).toHaveGraphqlOperation('HealthIndicatorWidget'); + }); + + expect(setShowHealthIndicator).toHaveBeenCalledWith(false); + expect(container).toBeEmptyDOMElement(); + expect(queryByText('MPD Health Indicator')).not.toBeInTheDocument(); + }); + + it('shows the health indicator if data', async () => { + render( + , + ); + + await waitFor(() => { + expect(mutationSpy).toHaveGraphqlOperation('HealthIndicatorWidget'); + }); + expect(setShowHealthIndicator).toHaveBeenCalledWith(true); + }); + + it('renders the data correctly', async () => { + const { findByText, getByText } = render( + , + ); + + expect(await findByText('Ownership')).toBeInTheDocument(); + expect(getByText('MPD Health Indicator')).toBeInTheDocument(); + + expect(getByText('90')).toBeInTheDocument(); + expect(getByText('Overall Health Indicator')).toBeInTheDocument(); + + expect(getByText('80')).toBeInTheDocument(); + expect(getByText('Ownership')).toBeInTheDocument(); + + expect(getByText('70')).toBeInTheDocument(); + expect(getByText('Consistency')).toBeInTheDocument(); + + expect(getByText('60')).toBeInTheDocument(); + expect(getByText('Success')).toBeInTheDocument(); + + expect(getByText('50')).toBeInTheDocument(); + expect(getByText('Depth')).toBeInTheDocument(); + }); + + describe('setUsingMachineCalculatedGoal', () => { + it('should set to TRUE as machine goal is defined and the same as the monthly goal', async () => { + const { findByText } = render( + , + ); + + expect(await findByText('Ownership')).toBeInTheDocument(); + expect(setUsingMachineCalculatedGoal).toHaveBeenCalledWith(true); + }); + + it('should set to FALSE as machine goal is different than the monthly goal', async () => { + const { findByText } = render( + , + ); + + expect(await findByText('Ownership')).toBeInTheDocument(); + expect(setUsingMachineCalculatedGoal).toHaveBeenCalledWith(false); + }); + it('should set to FALSE as machine goal is not defined', async () => { + const { findByText } = render( + , + ); + + expect(await findByText('Ownership')).toBeInTheDocument(); + expect(setUsingMachineCalculatedGoal).toHaveBeenCalledWith(false); + }); + }); +}); diff --git a/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.tsx b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.tsx new file mode 100644 index 000000000..e6cd4d2f2 --- /dev/null +++ b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget.tsx @@ -0,0 +1,150 @@ +import NextLink from 'next/link'; +import React, { Dispatch, SetStateAction, useEffect } from 'react'; +import { + Box, + Button, + CardActions, + CardContent, + CardHeader, + Grid, + Tooltip, + Typography, +} from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { DateTime } from 'luxon'; +import { useTranslation } from 'react-i18next'; +import AnimatedCard from 'src/components/AnimatedCard'; +import StyledProgress from 'src/components/StyledProgress'; +import { useHealthIndicatorWidgetQuery } from './HealthIndicatorWidget.generated'; +import { WidgetStat } from './WidgetStat/WidgetStat'; + +const StyledCardContent = styled(CardContent)(({ theme }) => ({ + padding: theme.spacing(2), + paddingBottom: 0, + height: 'calc(100% - 103px)', +})); + +const StyledBox = styled(Box)(() => ({ + display: 'flex', + gap: 2, + justifyContent: 'space-between', + alignItems: 'center', +})); + +interface HealthIndicatorWidgetProps { + accountListId: string; + goal: number; + showHealthIndicator: boolean; + setShowHealthIndicator: Dispatch>; + setUsingMachineCalculatedGoal: Dispatch>; +} + +export const HealthIndicatorWidget: React.FC = ({ + accountListId, + goal, + showHealthIndicator, + setShowHealthIndicator, + setUsingMachineCalculatedGoal, +}) => { + const { t } = useTranslation(); + + const { data, loading } = useHealthIndicatorWidgetQuery({ + variables: { + accountListId, + month: DateTime.now().startOf('month').toISODate(), + }, + }); + + useEffect(() => { + setShowHealthIndicator(!!data?.healthIndicatorData.length); + const machineCalculatedGoal = + data?.healthIndicatorData[0]?.machineCalculatedGoal; + setUsingMachineCalculatedGoal( + !!machineCalculatedGoal && goal === machineCalculatedGoal, + ); + }, [data, goal]); + + if (!showHealthIndicator) { + return null; + } + + const currentStats = data?.healthIndicatorData[0]; + + return ( + + + + + + + {currentStats?.overallHi} + + + + {t('Overall Health Indicator')} + + + + + + + + + + + + + + + + + + ); +}; diff --git a/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/WidgetStat/WidgetStat.tsx b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/WidgetStat/WidgetStat.tsx new file mode 100644 index 000000000..88f7890b6 --- /dev/null +++ b/src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/WidgetStat/WidgetStat.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { Box, Grid, Skeleton, Tooltip, Typography } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import { Maybe } from 'src/graphql/types.generated'; + +const WidgetStatGrid = styled(Grid)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.5), + marginBottom: theme.spacing(1.5), +})); + +interface WidgetStatProps { + loading: boolean; + stat?: Maybe; + statName: string; + toolTip: string; +} + +export const WidgetStat: React.FC = ({ + loading, + stat, + statName, + toolTip, +}) => ( + + {loading ? ( + + ) : ( + + + {stat} + + {statName} + + + + )} + +); diff --git a/src/components/StyledProgress/StyledProgress.test.tsx b/src/components/StyledProgress/StyledProgress.test.tsx index 220cda1a1..968ebd0b0 100644 --- a/src/components/StyledProgress/StyledProgress.test.tsx +++ b/src/components/StyledProgress/StyledProgress.test.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { render } from '@testing-library/react'; +import { formatPercentage } from './StyledProgress'; import StyledProgress from '.'; describe('StyledProgress', () => { @@ -37,4 +38,15 @@ describe('StyledProgress', () => { expect(getByText('/')).toBeInTheDocument(); expect(getByText('committedBelow')).toBeInTheDocument(); }); + + describe('formatPercentage()', () => { + it('should return the correct percentages', () => { + expect(formatPercentage(0)).toEqual('0%'); + expect(formatPercentage(0.1)).toEqual('10%'); + expect(formatPercentage(0.85)).toEqual('85%'); + expect(formatPercentage(1)).toEqual('100%'); + expect(formatPercentage(77)).toEqual('77%'); + expect(formatPercentage(0.3)).toEqual('30%'); + }); + }); }); diff --git a/src/components/StyledProgress/StyledProgress.tsx b/src/components/StyledProgress/StyledProgress.tsx index d7ca20d1a..20027c1dd 100644 --- a/src/components/StyledProgress/StyledProgress.tsx +++ b/src/components/StyledProgress/StyledProgress.tsx @@ -1,48 +1,55 @@ import React, { ReactElement } from 'react'; -import { Box, Skeleton, Theme, Typography } from '@mui/material'; +import { Box, Skeleton, Typography } from '@mui/material'; +import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; -import { makeStyles } from 'tss-react/mui'; -import { useLocale } from 'src/hooks/useLocale'; -import { percentageFormat } from '../../lib/intlFormat'; import MinimalSpacingTooltip from '../Shared/MinimalSpacingTooltip'; -const useStyles = makeStyles()((theme: Theme) => ({ - box: { - width: '100%', - height: '54px', - border: '2px solid #999999', - borderRadius: '50px', - padding: '2px', - position: 'relative', - marginBottom: theme.spacing(2), - display: 'flex', - alignItems: 'center', - }, - progress: { - position: 'absolute', - left: '2px', - height: '46px', - minWidth: '46px', - maxWidth: '99.6%', - borderRadius: '46px', - transition: 'width 1s ease-out', - width: '0%', - }, - skeleton: { - borderRadius: '46px', - height: '46px', - transform: 'none', - }, - primary: { - background: 'linear-gradient(180deg, #FFE67C 0%, #FFCF07 100%)', - }, - secondary: { - border: '5px solid #FFCF07', - }, - inline: { - display: 'inline', - }, - belowDetails: { position: 'absolute', right: '5px' }, +const ProgressBoxContainer = styled(Box, { + shouldForwardProp: (prop) => prop !== 'barHeight', +})<{ barHeight: number }>(({ barHeight, theme }) => ({ + width: '100%', + height: barHeight + 8, + border: '2px solid #999999', + borderRadius: '50px', + padding: '2px', + position: 'relative', + marginBottom: theme.spacing(2), + display: 'flex', + alignItems: 'center', +})); + +const ProgressBar = styled(Box, { + shouldForwardProp: (prop) => prop !== 'barHeight' && prop !== 'isPrimary', +})<{ barHeight: number; isPrimary: boolean }>(({ barHeight, isPrimary }) => ({ + position: 'absolute', + left: '2px', + height: barHeight, + minWidth: barHeight, + maxWidth: '99.6%', + borderRadius: barHeight, + transition: 'width 1s ease-out', + width: '0%', + background: isPrimary + ? 'linear-gradient(180deg, #FFE67C 0%, #FFCF07 100%)' + : 'initial', + border: isPrimary ? 'none' : '5px solid #FFCF07', +})); + +const StyledSkeleton = styled(Skeleton, { + shouldForwardProp: (prop) => prop !== 'barHeight', +})<{ barHeight: number }>(({ barHeight }) => ({ + borderRadius: barHeight, + height: barHeight, + transform: 'none', +})); + +const InlineTypography = styled(Typography)(() => ({ + display: 'inline', +})); + +const BelowDetailsBox = styled(Box)(() => ({ + position: 'absolute', + right: '5px', })); interface Props { @@ -51,61 +58,73 @@ interface Props { secondary?: number; receivedBelow?: string; committedBelow?: string; + barHeight?: number; } +export const formatPercentage = (value: number): string => { + if (value > 1) { + return `${value}%`; // Treat as a percentage + } + if (value >= 0) { + return `${value * 100}%`; // Convert 0.* and 1 to percentage + } + return '0%'; // Default to 0% +}; + const StyledProgress = ({ loading, primary = 0, secondary = 0, receivedBelow = '', committedBelow = '', + barHeight = 46, }: Props): ReactElement => { - const locale = useLocale(); const { t } = useTranslation(); - const { classes } = useStyles(); return ( - + {loading ? ( - ) : ( <> - - )} - + {receivedBelow && ( - {receivedBelow} + {receivedBelow} )} {committedBelow && receivedBelow && ( - {' / '} + {' / '} )} {committedBelow && ( - {committedBelow} + {committedBelow} )} - - + + ); }; diff --git a/src/components/Task/MassActions/AddTags/MassActionsTasksAddTagsModal.tsx b/src/components/Task/MassActions/AddTags/MassActionsTasksAddTagsModal.tsx index 67d76aff2..8cb0399d0 100644 --- a/src/components/Task/MassActions/AddTags/MassActionsTasksAddTagsModal.tsx +++ b/src/components/Task/MassActions/AddTags/MassActionsTasksAddTagsModal.tsx @@ -47,6 +47,7 @@ const NewTagInstructions = styled(Typography)(() => ({ const ExistingTagButton = styled(Button)(() => ({ textTransform: 'none', width: 'fit-content', + justifyContent: 'left', })); const AddTagIcon = styled(Add)(() => ({ diff --git a/src/components/Task/MassActions/RemoveTags/MassActionsTasksRemoveTagsModal.tsx b/src/components/Task/MassActions/RemoveTags/MassActionsTasksRemoveTagsModal.tsx index 32335d09e..706c53f82 100644 --- a/src/components/Task/MassActions/RemoveTags/MassActionsTasksRemoveTagsModal.tsx +++ b/src/components/Task/MassActions/RemoveTags/MassActionsTasksRemoveTagsModal.tsx @@ -39,6 +39,7 @@ interface MassActionsTasksRemoveTagsModalProps { const ExistingTagButton = styled(Button)(() => ({ textTransform: 'none', width: 'fit-content', + justifyContent: 'left', })); const SelectedTagButton = styled(Button)(() => ({ @@ -115,7 +116,7 @@ export const MassActionsTasksRemoveTagsModal: React.FC< const tagsData = tasksForTags?.tasks.nodes.map((task) => task.tagList) ?? []; - const contactsTagsList = [...new Set([...tagsData.flatMap((el) => el)])]; + const contactsTagsList = Array.from(new Set(tagsData.flat())).sort(); return ( diff --git a/src/utils/sourceHelper.ts b/src/utils/sourceHelper.ts index f4942d84e..d2207ae6d 100644 --- a/src/utils/sourceHelper.ts +++ b/src/utils/sourceHelper.ts @@ -26,7 +26,7 @@ export const sourceToStr = ( } }; -export const editableSources = ['MPDX', 'TntImport']; +export const editableSources = ['MPDX', 'TntImport', 'GoogleImport']; export const isEditableSource = (source: string | undefined): boolean => { // A source is editable if it is undefined or if it is in the list of editable sources. diff --git a/yarn.lock b/yarn.lock index 00aabf2f2..8070d9b3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6187,21 +6187,21 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^7.5.0": - version: 7.5.0 - resolution: "@typescript-eslint/parser@npm:7.5.0" - dependencies: - "@typescript-eslint/scope-manager": 7.5.0 - "@typescript-eslint/types": 7.5.0 - "@typescript-eslint/typescript-estree": 7.5.0 - "@typescript-eslint/visitor-keys": 7.5.0 +"@typescript-eslint/parser@npm:^8.17.0": + version: 8.17.0 + resolution: "@typescript-eslint/parser@npm:8.17.0" + dependencies: + "@typescript-eslint/scope-manager": 8.17.0 + "@typescript-eslint/types": 8.17.0 + "@typescript-eslint/typescript-estree": 8.17.0 + "@typescript-eslint/visitor-keys": 8.17.0 debug: ^4.3.4 peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 peerDependenciesMeta: typescript: optional: true - checksum: c9f85ae638e27fb249e565fc299cfee456b15f6a67156f373c10fa6e310a470b5298e174ca75309789c4cf1acd1a9fd3391d5fe128208b05bac0b701d5ddf512 + checksum: 3d330fc777cc34d8f21c7668a6ef48a1ce91905efde000f561cde76a630433c2a76d46857f7a62dc23c530e6441a5ea18b89c52e7fa5a099144c54bfe646dc35 languageName: node linkType: hard @@ -6225,6 +6225,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.17.0": + version: 8.17.0 + resolution: "@typescript-eslint/scope-manager@npm:8.17.0" + dependencies: + "@typescript-eslint/types": 8.17.0 + "@typescript-eslint/visitor-keys": 8.17.0 + checksum: c5f628e5b4793181a219fc8be4dc2653b2a2a158c4add645b3ba063b9618f5892e5bbf6726c9e674731e698a3df4f2ddb671494482e0f59b6625c43810f78eeb + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:7.5.0": version: 7.5.0 resolution: "@typescript-eslint/type-utils@npm:7.5.0" @@ -6256,6 +6266,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.17.0": + version: 8.17.0 + resolution: "@typescript-eslint/types@npm:8.17.0" + checksum: 5f6933903ce4af536f180c9e326c18da715f6f400e6bc5b89828dcb5779ae5693bf95c59d253e105c9efe6ffd2046d0db868bcfb1c5288c5e194bae4ebaa9976 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.33.1": version: 5.33.1 resolution: "@typescript-eslint/typescript-estree@npm:5.33.1" @@ -6293,6 +6310,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.17.0": + version: 8.17.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.17.0" + dependencies: + "@typescript-eslint/types": 8.17.0 + "@typescript-eslint/visitor-keys": 8.17.0 + debug: ^4.3.4 + fast-glob: ^3.3.2 + is-glob: ^4.0.3 + minimatch: ^9.0.4 + semver: ^7.6.0 + ts-api-utils: ^1.3.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 35d3dca3cde7a1f3a7a1e4e5a25a69b6151338cd329dceeb52880e6f05048d10c9ac472a07e558fdfb7acc10dd60cd106284e834cfe40ced3d2c4527e8727335 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:7.5.0": version: 7.5.0 resolution: "@typescript-eslint/utils@npm:7.5.0" @@ -6346,6 +6382,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.17.0": + version: 8.17.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.17.0" + dependencies: + "@typescript-eslint/types": 8.17.0 + eslint-visitor-keys: ^4.2.0 + checksum: f92f659ec88a1ce34f5003722a133ced1ebf9b3dfc1c0ff18caa5362d4722307edb42fa606ebf80aada8525abe78b24143ef93864d38a1e359605096f1fe2f00 + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" @@ -10402,6 +10448,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^4.2.0": + version: 4.2.0 + resolution: "eslint-visitor-keys@npm:4.2.0" + checksum: 779c604672b570bb4da84cef32f6abb085ac78379779c1122d7879eade8bb38ae715645324597cf23232d03cef06032c9844d25c73625bc282a5bfd30247e5b5 + languageName: node + linkType: hard + "eslint@npm:^8.57.0": version: 8.57.0 resolution: "eslint@npm:8.57.0" @@ -10832,6 +10885,19 @@ __metadata: languageName: node linkType: hard +"fast-glob@npm:^3.3.2": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" + dependencies: + "@nodelib/fs.stat": ^2.0.2 + "@nodelib/fs.walk": ^1.2.3 + glob-parent: ^5.1.2 + merge2: ^1.3.0 + micromatch: ^4.0.4 + checksum: 900e4979f4dbc3313840078419245621259f349950411ca2fa445a2f9a1a6d98c3b5e7e0660c5ccd563aa61abe133a21765c6c0dec8e57da1ba71d8000b05ec1 + languageName: node + linkType: hard + "fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" @@ -14895,6 +14961,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: ^2.0.1 + checksum: 2c035575eda1e50623c731ec6c14f65a85296268f749b9337005210bb2b34e2705f8ef1a358b188f69892286ab99dc42c8fb98a57bde55c8d81b3023c19cea28 + languageName: node + linkType: hard + "minimist@npm:^1.2.0, minimist@npm:^1.2.6": version: 1.2.6 resolution: "minimist@npm:1.2.6" @@ -15084,7 +15159,7 @@ __metadata: "@types/testing-library__jest-dom": ^5.14.5 "@types/uuid": ^9.0.1 "@typescript-eslint/eslint-plugin": ^7.5.0 - "@typescript-eslint/parser": ^7.5.0 + "@typescript-eslint/parser": ^8.17.0 apollo-datasource-rest: ^3.7.0 apollo-server-micro: ^3.11.0 apollo3-cache-persist: ^0.14.1 @@ -17990,7 +18065,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.6.3": +"semver@npm:^7.6.0, semver@npm:^7.6.3": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -19503,6 +19578,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^1.3.0": + version: 1.4.3 + resolution: "ts-api-utils@npm:1.4.3" + peerDependencies: + typescript: ">=4.2.0" + checksum: ea00dee382d19066b2a3d8929f1089888b05fec797e32e7a7004938eda1dccf2e77274ee2afcd4166f53fab9b8d7ee90ebb225a3183f9ba8817d636f688a148d + languageName: node + linkType: hard + "ts-essentials@npm:^9.3.0": version: 9.3.0 resolution: "ts-essentials@npm:9.3.0"