diff --git a/.eslintrc.js b/.eslintrc.js index f964eb8277..6b12d72670 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,56 +1,56 @@ module.exports = { - parser: '@typescript-eslint/parser', // Specifies the ESLint parser - extends: [ - 'plugin:jest/recommended', - 'plugin:import/errors', - 'plugin:import/warnings', - 'plugin:import/typescript', - 'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react - 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin - 'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier - 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. - ], - parserOptions: { - ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features - sourceType: 'module', // Allows for the use of imports + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + extends: [ + 'plugin:jest/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react + 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin + 'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier + 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. + ], + parserOptions: { + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + }, + rules: { + '@typescript-eslint/camelcase': 'off', + 'import/no-duplicates': 'error', + 'import/extensions': 'error', + 'import/order': 'error', + 'import/newline-after-import': 'error', + 'import/prefer-default-export': 'error', + 'import/no-named-default': 'error', + 'import/no-anonymous-default-export': 'error', + 'import/dynamic-import-chunkname': 'error', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + }, + settings: { + react: { + version: 'detect', }, - rules: { - '@typescript-eslint/camelcase': 'off', - 'import/no-duplicates': 'error', - 'import/extensions': 'error', - 'import/order': 'error', - 'import/newline-after-import': 'error', - 'import/prefer-default-export': 'error', - 'import/no-named-default': 'error', - 'import/no-anonymous-default-export': 'error', - 'import/dynamic-import-chunkname': 'error', - '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + }, + overrides: [ + { + files: ['*.js'], + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-var-requires': 'off', + }, }, - settings: { - react: { - version: 'detect', - }, + { + files: ['*.stories.tsx', '*.test.tsx', '__tests__/**.*'], + rules: { + 'import/no-anonymous-default-export': 'off', + '@typescript-eslint/no-empty-function': 'off', + }, }, - overrides: [ - { - files: ['*.js'], - rules: { - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-var-requires': 'off', - }, - }, - { - files: ['*.stories.tsx', '*.test.tsx', '__tests__/**.*'], - rules: { - 'import/no-anonymous-default-export': 'off', - '@typescript-eslint/no-empty-function': 'off', - }, - }, - { - files: ['onesky/**.ts'], - rules: { - '@typescript-eslint/no-var-requires': 'off', - }, - }, - ], + { + files: ['onesky/**.ts'], + rules: { + '@typescript-eslint/no-var-requires': 'off', + }, + }, + ], }; diff --git a/.github/workflows/merge-pr-into-stage.yml b/.github/workflows/merge-pr-into-stage.yml index d720e8f8d5..e69b820b69 100644 --- a/.github/workflows/merge-pr-into-stage.yml +++ b/.github/workflows/merge-pr-into-stage.yml @@ -1,18 +1,18 @@ name: Merge PR into stage on: - pull_request: - types: [labeled, synchronize] + pull_request: + types: [labeled, synchronize] jobs: - merge-branch: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - name: Merge by labeled - uses: devmasx/merge-branch@v1.1.0 - if: contains( github.event.pull_request.labels.*.name, 'merged into stage') - with: - type: now - target_branch: 'stage' - env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + merge-branch: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Merge by labeled + uses: devmasx/merge-branch@v1.1.0 + if: contains( github.event.pull_request.labels.*.name, 'merged into stage') + with: + type: now + target_branch: 'stage' + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.gitignore b/.gitignore index 78e7bcd92d..6b8438f35e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,12 +15,7 @@ /node_modules/.cache # next.js -/.next/ -/out/ -/dist/ - -# production -/build +/.next # misc .DS_Store diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..9b79d72c07 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,17 @@ +# dependencies +.yarn +.pnp.js + +# testing +/coverage + +# Storybook cache +/node_modules/.cache + +# next.js +/.next + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/.prettierrc.js b/.prettierrc.js index 2c66e64e9b..de2f53cdf9 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,7 +1,4 @@ module.exports = { - semi: true, - trailingComma: "all", singleQuote: true, - printWidth: 120, - tabWidth: 4 + trailingComma: 'all', }; diff --git a/.storybook/main.js b/.storybook/main.js index 6a043b5a98..9dec2ed020 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,23 +1,23 @@ const path = require('path'); module.exports = { - webpackFinal: async (config) => { - config.module.rules.push({ - test: /\.(graphql|gql)$/, - include: path.resolve(__dirname, '../'), - exclude: /node_modules/, - use: [ - { - loader: 'graphql-tag/loader', - }, - ], - }); - return config; - }, - addons: [ - '@storybook/addon-actions/register', - '@storybook/addon-viewport/register', - '@storybook/addon-knobs/register', - 'storybook-addon-i18next/register', - ], + webpackFinal: async (config) => { + config.module.rules.push({ + test: /\.(graphql|gql)$/, + include: path.resolve(__dirname, '../'), + exclude: /node_modules/, + use: [ + { + loader: 'graphql-tag/loader', + }, + ], + }); + return config; + }, + addons: [ + '@storybook/addon-actions/register', + '@storybook/addon-viewport/register', + '@storybook/addon-knobs/register', + 'storybook-addon-i18next/register', + ], }; diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index de59e2ff48..642a87b2ac 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -1,8 +1,11 @@ - + diff --git a/.storybook/preview.js b/.storybook/preview.js index 84a9ab9508..f3ed747a98 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -16,37 +16,40 @@ import theme from '../src/theme'; import i18n from '../src/lib/i18n'; if (isChromatic()) { - MockDate.set(new Date(2020, 1, 1)); + MockDate.set(new Date(2020, 1, 1)); } addDecorator( - withI18next({ - i18n, - languages: { - en: 'English', - de: 'German', - }, - }), + withI18next({ + i18n, + languages: { + en: 'English', + de: 'German', + }, + }), ); addDecorator(withKnobs); addDecorator((StoryFn) => ( - - - - - - - - - - - - - - + + + + + + + + + + + + + + )); addParameters({ chromatic: { diffThreshold: true } }); // automatically import all files ending in *.stories.tsx -configure(require.context('../src/components', true, /\.stories\.tsx?$/), module); +configure( + require.context('../src/components', true, /\.stories\.tsx?$/), + module, +); diff --git a/.travis.yml b/.travis.yml index 48d7e50127..d725f6fc89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,23 @@ sudo: false language: node_js node_js: - - '15' + - '15' branches: - only: - - main + only: + - main install: - - yarn install --immutable --immutable-cache --check-cache + - yarn install --immutable --immutable-cache --check-cache jobs: - include: - - stage: Test - script: - - yarn test - - stage: Lint - script: - - yarn lint:ci - - stage: Chromatic - script: - - bin/travis_chromatic.sh + include: + - stage: Test + script: + - yarn test + - stage: Lint + script: + - yarn lint:ci + - stage: Prettier Check + script: + - yarn prettier:check + - stage: Chromatic + script: + - bin/travis_chromatic.sh diff --git a/.vscode/launch.json b/.vscode/launch.json index 922552cb7b..16de7a23f2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,19 +1,19 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "name": "vscode-jest-tests", - "request": "launch", - "runtimeExecutable": "yarn", - "runtimeArgs": ["run", "--inspect-brk", "jest", "--runInBand"], - "cwd": "${workspaceFolder}", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "disableOptimisticBPs": true - } - ] + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "name": "vscode-jest-tests", + "request": "launch", + "runtimeExecutable": "yarn", + "runtimeArgs": ["run", "--inspect-brk", "jest", "--runInBand"], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true + } + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 8907a15d92..31c9188c3b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,16 +1,21 @@ { - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, - "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], - "editor.formatOnSave": true, - "typescript.tsdk": ".yarn/sdks/typescript/lib", - "search.exclude": { - "**/.yarn": true, - "**/.pnp.*": true - }, - "eslint.nodePath": ".yarn/sdks", - "prettier.prettierPath": ".yarn/sdks/prettier/index.js", - "typescript.enablePromptUseWorkspaceTsdk": true, - "jest.pathToJest": "yarn jest" + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "editor.formatOnSave": true, + "typescript.tsdk": ".yarn/sdks/typescript/lib", + "search.exclude": { + "**/.yarn": true, + "**/.pnp.*": true + }, + "eslint.nodePath": ".yarn/sdks", + "prettier.prettierPath": ".yarn/sdks/prettier/index.js", + "typescript.enablePromptUseWorkspaceTsdk": true, + "jest.pathToJest": "yarn jest" } diff --git a/.yarnrc.yml b/.yarnrc.yml index 416372e15a..1d3ccdbf89 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,5 +1,5 @@ plugins: - - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs - spec: '@yarnpkg/plugin-interactive-tools' + - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs + spec: '@yarnpkg/plugin-interactive-tools' yarnPath: .yarn/releases/yarn-berry.cjs diff --git a/README.md b/README.md index ea24652151..48df9f4913 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the To learn more about Next.js, take a look at the following resources: -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. You can check out [the Next.js GitHub repository](https://github.com/zeit/next.js/) - your feedback and contributions are welcome! diff --git a/__tests__/pages/accountLists/[accountListId]/tasks.test.tsx b/__tests__/pages/accountLists/[accountListId]/tasks.test.tsx index f197af665e..1f7f802fd3 100644 --- a/__tests__/pages/accountLists/[accountListId]/tasks.test.tsx +++ b/__tests__/pages/accountLists/[accountListId]/tasks.test.tsx @@ -1,55 +1,69 @@ import { initialFilterFromPath } from '../../../../pages/accountLists/[accountListId]/tasks'; describe('tasks', () => { - describe('initialFilterFromPath', () => { - it('returns completed', () => { - expect(initialFilterFromPath('/tasks?completed=true')).toEqual({ completed: true }); - }); - it('returns wildcardSearch', () => { - expect(initialFilterFromPath('/tasks?wildcardSearch=true+words')).toEqual({ wildcardSearch: 'true words' }); - }); - it('returns startAt[max]', () => { - expect(initialFilterFromPath('/tasks?startAt[max]=2020-10-19')).toEqual({ - startAt: { max: '2020-10-19' }, - }); - }); + describe('initialFilterFromPath', () => { + it('returns completed', () => { + expect(initialFilterFromPath('/tasks?completed=true')).toEqual({ + completed: true, + }); + }); + it('returns wildcardSearch', () => { + expect( + initialFilterFromPath('/tasks?wildcardSearch=true+words'), + ).toEqual({ wildcardSearch: 'true words' }); + }); + it('returns startAt[max]', () => { + expect(initialFilterFromPath('/tasks?startAt[max]=2020-10-19')).toEqual({ + startAt: { max: '2020-10-19' }, + }); + }); - it('returns startAt[min]', () => { - expect(initialFilterFromPath('/tasks?startAt[min]=2020-10-19')).toEqual({ - startAt: { min: '2020-10-19' }, - }); - }); + it('returns startAt[min]', () => { + expect(initialFilterFromPath('/tasks?startAt[min]=2020-10-19')).toEqual({ + startAt: { min: '2020-10-19' }, + }); + }); - it('returns startAt[max] and startAt[min]', () => { - expect(initialFilterFromPath('/tasks?startAt[min]=2020-05-19&startAt[max]=2020-10-19')).toEqual({ - startAt: { min: '2020-05-19', max: '2020-10-19' }, - }); - }); + it('returns startAt[max] and startAt[min]', () => { + expect( + initialFilterFromPath( + '/tasks?startAt[min]=2020-05-19&startAt[max]=2020-10-19', + ), + ).toEqual({ + startAt: { min: '2020-05-19', max: '2020-10-19' }, + }); + }); - it('returns userIds', () => { - expect(initialFilterFromPath('/tasks?userIds[]=abc&userIds[]=def')).toEqual({ - userIds: ['abc', 'def'], - }); - }); + it('returns userIds', () => { + expect( + initialFilterFromPath('/tasks?userIds[]=abc&userIds[]=def'), + ).toEqual({ + userIds: ['abc', 'def'], + }); + }); - it('returns tags', () => { - expect(initialFilterFromPath('/tasks?tags[]=abc&tags[]=def')).toEqual({ - tags: ['abc', 'def'], - }); - }); + it('returns tags', () => { + expect(initialFilterFromPath('/tasks?tags[]=abc&tags[]=def')).toEqual({ + tags: ['abc', 'def'], + }); + }); - it('returns contactIds', () => { - expect(initialFilterFromPath('/tasks?contactIds[]=abc&contactIds[]=def')).toEqual({ - contactIds: ['abc', 'def'], - }); - }); + it('returns contactIds', () => { + expect( + initialFilterFromPath('/tasks?contactIds[]=abc&contactIds[]=def'), + ).toEqual({ + contactIds: ['abc', 'def'], + }); + }); - it('returns activityType', () => { - expect( - initialFilterFromPath('/tasks?activityType[]=PARTNER_FINANCIAL&activityType[]=PARTNER_PRAYER'), - ).toEqual({ - activityType: ['PARTNER_FINANCIAL', 'PARTNER_PRAYER'], - }); - }); + it('returns activityType', () => { + expect( + initialFilterFromPath( + '/tasks?activityType[]=PARTNER_FINANCIAL&activityType[]=PARTNER_PRAYER', + ), + ).toEqual({ + activityType: ['PARTNER_FINANCIAL', 'PARTNER_PRAYER'], + }); }); + }); }); diff --git a/__tests__/pages/api/handoff.test.ts b/__tests__/pages/api/handoff.test.ts index 4a6ab476d9..329f06ce96 100644 --- a/__tests__/pages/api/handoff.test.ts +++ b/__tests__/pages/api/handoff.test.ts @@ -5,95 +5,99 @@ import handoff from '../../../pages/api/handoff'; jest.mock('next-auth/jwt', () => ({})); describe('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/api/handoff', () => { + beforeEach(() => { + jwt.getToken = jest.fn(); + }); + + it('returns 422', async () => { + const { req, res } = createMocks({ method: 'GET' }); + await handoff(req, res); + + expect(res._getStatusCode()).toBe(422); + }); + + describe('session', () => { beforeEach(() => { - jwt.getToken = jest.fn(); + jwt.getToken = jest.fn().mockReturnValue({ token: 'accessToken' }); }); - it('returns 422', async () => { - const { req, res } = createMocks({ method: 'GET' }); - await handoff(req, res); - - expect(res._getStatusCode()).toBe(422); + it('returns redirect', async () => { + const { req, res } = createMocks({ + method: 'GET', + query: { + accountListId: 'accountListId', + userId: 'userId', + path: 'path', + }, + }); + await handoff(req, res); + + expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + 'https://stage.mpdx.org/handoff?accessToken=accessToken&accountListId=accountListId&userId=userId&path=path', + ); }); - describe('session', () => { - beforeEach(() => { - jwt.getToken = jest.fn().mockReturnValue({ token: 'accessToken' }); - }); + it('returns redirect for auth', async () => { + const { req, res } = createMocks({ + method: 'GET', + query: { + path: 'auth/user/admin', + auth: 'true', + }, + }); + await handoff(req, res); + + expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + 'https://auth.stage.mpdx.org/auth/user/admin?access_token=accessToken', + ); + }); - it('returns redirect', async () => { - const { req, res } = createMocks({ - method: 'GET', - query: { - accountListId: 'accountListId', - userId: 'userId', - path: 'path', - }, - }); - await handoff(req, res); - - expect(res._getStatusCode()).toBe(302); - expect(res._getRedirectUrl()).toBe( - 'https://stage.mpdx.org/handoff?accessToken=accessToken&accountListId=accountListId&userId=userId&path=path', - ); + describe('SITE_URL set', () => { + const OLD_ENV = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...OLD_ENV, SITE_URL: 'https://next.mpdx.org' }; + }); + + afterAll(() => { + process.env = OLD_ENV; + }); + + it('returns redirect', async () => { + const { req, res } = createMocks({ + method: 'GET', + query: { + accountListId: 'accountListId', + userId: 'userId', + path: 'path', + }, }); + await handoff(req, res); - it('returns redirect for auth', async () => { - const { req, res } = createMocks({ - method: 'GET', - query: { - path: 'auth/user/admin', - auth: 'true', - }, - }); - await handoff(req, res); - - expect(res._getStatusCode()).toBe(302); - expect(res._getRedirectUrl()).toBe('https://auth.stage.mpdx.org/auth/user/admin?access_token=accessToken'); + expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + 'https://mpdx.org/handoff?accessToken=accessToken&accountListId=accountListId&userId=userId&path=path', + ); + }); + + it('returns redirect for auth', async () => { + const { req, res } = createMocks({ + method: 'GET', + query: { + path: 'auth/user/admin', + auth: 'true', + }, }); + await handoff(req, res); - describe('SITE_URL set', () => { - const OLD_ENV = process.env; - - beforeEach(() => { - jest.resetModules(); - process.env = { ...OLD_ENV, SITE_URL: 'https://next.mpdx.org' }; - }); - - afterAll(() => { - process.env = OLD_ENV; - }); - - it('returns redirect', async () => { - const { req, res } = createMocks({ - method: 'GET', - query: { - accountListId: 'accountListId', - userId: 'userId', - path: 'path', - }, - }); - await handoff(req, res); - - expect(res._getStatusCode()).toBe(302); - expect(res._getRedirectUrl()).toBe( - 'https://mpdx.org/handoff?accessToken=accessToken&accountListId=accountListId&userId=userId&path=path', - ); - }); - - it('returns redirect for auth', async () => { - const { req, res } = createMocks({ - method: 'GET', - query: { - path: 'auth/user/admin', - auth: 'true', - }, - }); - await handoff(req, res); - - expect(res._getStatusCode()).toBe(302); - expect(res._getRedirectUrl()).toBe('https://auth.mpdx.org/auth/user/admin?access_token=accessToken'); - }); - }); + expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + 'https://auth.mpdx.org/auth/user/admin?access_token=accessToken', + ); + }); }); + }); }); diff --git a/__tests__/util/TestRouter.tsx b/__tests__/util/TestRouter.tsx index fbb1ddd97a..af723fd765 100644 --- a/__tests__/util/TestRouter.tsx +++ b/__tests__/util/TestRouter.tsx @@ -3,37 +3,41 @@ import Router, { NextRouter, Router as IRouter } from 'next/router'; // eslint-d import { RouterContext } from 'next/dist/next-server/lib/router-context'; interface Props { - children: ReactNode; - router?: Partial; + children: ReactNode; + router?: Partial; } const TestRouter = ({ children, router = {} }: Props): ReactElement => { - const defaultRouter: NextRouter = { - basePath: '', - route: '', - pathname: '', - query: {}, - asPath: '', - push: async (): Promise => true, - replace: async (): Promise => true, - reload: (): void => null, - back: (): void => null, - prefetch: async (): Promise => undefined, - beforePopState: (): void => null, - isFallback: false, - isReady: false, - events: { - on: (): void => null, - off: (): void => null, - emit: (): void => null, - }, - }; + const defaultRouter: NextRouter = { + basePath: '', + route: '', + pathname: '', + query: {}, + asPath: '', + push: async (): Promise => true, + replace: async (): Promise => true, + reload: (): void => null, + back: (): void => null, + prefetch: async (): Promise => undefined, + beforePopState: (): void => null, + isFallback: false, + isReady: false, + events: { + on: (): void => null, + off: (): void => null, + emit: (): void => null, + }, + }; - const configuredRouter = { ...defaultRouter, ...router }; + const configuredRouter = { ...defaultRouter, ...router }; - Router.router = configuredRouter as IRouter; + Router.router = configuredRouter as IRouter; - return {children}; + return ( + + {children} + + ); }; export default TestRouter; diff --git a/__tests__/util/TestWrapper.tsx b/__tests__/util/TestWrapper.tsx index 5f04a21bde..612a1127dc 100644 --- a/__tests__/util/TestWrapper.tsx +++ b/__tests__/util/TestWrapper.tsx @@ -9,35 +9,35 @@ import { AppState } from '../../src/components/App/rootReducer'; import TestRouter from './TestRouter'; interface Props { - mocks?: MockedResponse[]; - children: ReactNode; - initialState?: Partial; - disableAppProvider?: boolean; - cache?: InMemoryCache; + mocks?: MockedResponse[]; + children: ReactNode; + initialState?: Partial; + disableAppProvider?: boolean; + cache?: InMemoryCache; } const TestWrapper = ({ - mocks = [], - children, - initialState = { accountListId: 'abc' }, - disableAppProvider = false, - cache = new InMemoryCache({ addTypename: false }), + mocks = [], + children, + initialState = { accountListId: 'abc' }, + disableAppProvider = false, + cache = new InMemoryCache({ addTypename: false }), }: Props): ReactElement => { - return ( - - - - - {disableAppProvider ? ( - <>{children} - ) : ( - {children} - )} - - - - - ); + return ( + + + + + {disableAppProvider ? ( + <>{children} + ) : ( + {children} + )} + + + + + ); }; export default TestWrapper; diff --git a/__tests__/util/fileMock.js b/__tests__/util/fileMock.js index 2fa3141d4d..192b18b37b 100644 --- a/__tests__/util/fileMock.js +++ b/__tests__/util/fileMock.js @@ -1,7 +1,7 @@ const path = require('path'); module.exports = { - process(_src, filename, _config, _options) { - return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; - }, + process(_src, filename, _config, _options) { + return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; + }, }; diff --git a/__tests__/util/globalSetup.ts b/__tests__/util/globalSetup.ts index 8999e19e89..690fccb203 100644 --- a/__tests__/util/globalSetup.ts +++ b/__tests__/util/globalSetup.ts @@ -1,5 +1,5 @@ const globalSetup = (): void => { - process.env.TZ = 'UTC'; + process.env.TZ = 'UTC'; }; export default globalSetup; diff --git a/__tests__/util/matchMediaMock.ts b/__tests__/util/matchMediaMock.ts index 07a1e78724..23b2548e0a 100644 --- a/__tests__/util/matchMediaMock.ts +++ b/__tests__/util/matchMediaMock.ts @@ -1,13 +1,13 @@ import mediaQuery, { MediaValues } from 'css-mediaquery'; const matchMediaMock = (options: Partial): void => { - window.matchMedia = (query: string): MediaQueryList => { - return ({ - matches: mediaQuery.match(query, options), - addListener: jest.fn(), - removeListener: jest.fn(), - } as unknown) as MediaQueryList; - }; + window.matchMedia = (query: string): MediaQueryList => { + return ({ + matches: mediaQuery.match(query, options), + addListener: jest.fn(), + removeListener: jest.fn(), + } as unknown) as MediaQueryList; + }; }; export default matchMediaMock; diff --git a/__tests__/util/setup.ts b/__tests__/util/setup.ts index 491c3f3daf..8554733d3f 100644 --- a/__tests__/util/setup.ts +++ b/__tests__/util/setup.ts @@ -1,13 +1,13 @@ import '@testing-library/jest-dom'; window.document.createRange = (): Range => - (({ - setStart: jest.fn(), - setEnd: jest.fn(), - commonAncestorContainer: ({ - nodeName: 'BODY', - ownerDocument: document, - } as unknown) as Node, - } as unknown) as Range); + (({ + setStart: jest.fn(), + setEnd: jest.fn(), + commonAncestorContainer: ({ + nodeName: 'BODY', + ownerDocument: document, + } as unknown) as Node, + } as unknown) as Range); window.HTMLElement.prototype.scrollIntoView = jest.fn(); diff --git a/__tests__/util/testingLibraryReactMock.tsx b/__tests__/util/testingLibraryReactMock.tsx index 8a217287a0..4e7aa24c6c 100644 --- a/__tests__/util/testingLibraryReactMock.tsx +++ b/__tests__/util/testingLibraryReactMock.tsx @@ -5,17 +5,19 @@ import i18n from '../../src/lib/i18n'; import translation from '../../public/locales/en/translation.json'; interface Props { - children: ReactNode; + children: ReactNode; } i18n.addResourceBundle('en', 'translation', translation); const Wrapper = ({ children }: Props): ReactElement => { - return {children}; + return {children}; }; -const customRender = (ui: ReactElement, options?: Omit): RenderResult => - render(ui, { wrapper: Wrapper, ...options }); +const customRender = ( + ui: ReactElement, + options?: Omit, +): RenderResult => render(ui, { wrapper: Wrapper, ...options }); export * from '@testing-library/react'; diff --git a/apollo.config.js b/apollo.config.js index 4a9381bd39..060934b473 100644 --- a/apollo.config.js +++ b/apollo.config.js @@ -1,8 +1,8 @@ module.exports = { - client: { - service: { - name: 'MPDX', - url: 'https://api.stage.mpdx.org/graphql', - }, + client: { + service: { + name: 'MPDX', + url: 'https://api.stage.mpdx.org/graphql', }, + }, }; diff --git a/babel.config.js b/babel.config.js index 3d3ac68e65..f94cf8370c 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,6 @@ // This file is here for Jest to pick up the "next/babel" preset module.exports = { - presets: ['next/babel'], - plugins: [], + presets: ['next/babel'], + plugins: [], }; diff --git a/i18next-parser.config.js b/i18next-parser.config.js index ccbf9cfbeb..cd61d31096 100644 --- a/i18next-parser.config.js +++ b/i18next-parser.config.js @@ -1,17 +1,17 @@ module.exports = { - indentation: 4, - lexers: { - js: ['JsxLexer'], - ts: ['JsxLexer'], - jsx: ['JsxLexer'], - tsx: ['JsxLexer'], - default: ['JsxLexer'], - }, - locales: ['en'], - output: 'public/locales/$LOCALE/$NAMESPACE.json', - input: ['src/**/*.{js,jsx,ts,tsx}'], - verbose: true, - nsSeparator: false, - keySeparator: false, - useKeysAsDefaultValue: true, + indentation: 4, + lexers: { + js: ['JsxLexer'], + ts: ['JsxLexer'], + jsx: ['JsxLexer'], + tsx: ['JsxLexer'], + default: ['JsxLexer'], + }, + locales: ['en'], + output: 'public/locales/$LOCALE/$NAMESPACE.json', + input: ['src/**/*.{js,jsx,ts,tsx}'], + verbose: true, + nsSeparator: false, + keySeparator: false, + useKeysAsDefaultValue: true, }; diff --git a/jest.config.js b/jest.config.js index 377bc400b4..c520fe58cc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,13 +1,13 @@ module.exports = { - roots: ['/src', '/__tests__/pages'], - globalSetup: '/__tests__/util/globalSetup.ts', - setupFilesAfterEnv: ['/__tests__/util/setup.ts'], - transform: { - '\\.[jt]sx?$': 'babel-jest', - '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': - '/__tests__/util/fileMock.js', - '\\.(gql|graphql)$': 'jest-transform-graphql', - }, - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - clearMocks: true, + roots: ['/src', '/__tests__/pages'], + globalSetup: '/__tests__/util/globalSetup.ts', + setupFilesAfterEnv: ['/__tests__/util/setup.ts'], + transform: { + '\\.[jt]sx?$': 'babel-jest', + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': + '/__tests__/util/fileMock.js', + '\\.(gql|graphql)$': 'jest-transform-graphql', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + clearMocks: true, }; diff --git a/next-env.d.ts b/next-env.d.ts index e27519e9bc..3b580c1601 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,10 +1,10 @@ /// /// declare module '*.graphql' { - import { DocumentNode } from 'graphql'; + import { DocumentNode } from 'graphql'; - const value: DocumentNode; - export = value; + const value: DocumentNode; + export = value; } declare module '*.svg'; diff --git a/next.config.js b/next.config.js index 125c08ab47..87f8a86e05 100644 --- a/next.config.js +++ b/next.config.js @@ -7,38 +7,43 @@ const prod = process.env.NODE_ENV === 'production'; let SiteUrl; if (process.env.SITE_URL) { - SiteUrl = process.env.SITE_URL; + SiteUrl = process.env.SITE_URL; } else if (process.env.VERCEL_URL) { - SiteUrl = `https://${process.env.VERCEL_URL}`; + SiteUrl = `https://${process.env.VERCEL_URL}`; } else { - SiteUrl = 'http://localhost:3000'; + SiteUrl = 'http://localhost:3000'; } const withBundleAnalyzer = require('@next/bundle-analyzer')({ - enabled: process.env.ANALYZE === 'true', + enabled: process.env.ANALYZE === 'true', }); module.exports = withPlugins([ - [ - withPWA, - { - pwa: { - dest: 'public', - disable: !prod, - }, - }, - ], - withOptimizedImages, - withGraphql, - withBundleAnalyzer, + [ + withPWA, { - env: { - JWT_SECRET: process.env.JWT_SECRET || 'aed8e0786376a2abe15f5c8f8e2ee74565d0915897b33296594bb1b549098ba7', - API_URL: process.env.API_URL || 'https://api.stage.mpdx.org/graphql', - SITE_URL: SiteUrl, - CLIENT_ID: process.env.CLIENT_ID || '4027334344069527005', - CLIENT_SECRET: process.env.CLIENT_SECRET || 'V3WBTfLMgXBuL6XNTPm13CIK7Cwvtb0VnQpeQH-Oojx6kuzaD7durA', - BEACON_TOKEN: process.env.BEACON_TOKEN || '01b4f5f0-7fff-492a-b5ec-d536f3657d10', - }, + pwa: { + dest: 'public', + disable: !prod, + }, }, + ], + withOptimizedImages, + withGraphql, + withBundleAnalyzer, + { + env: { + JWT_SECRET: + process.env.JWT_SECRET || + 'aed8e0786376a2abe15f5c8f8e2ee74565d0915897b33296594bb1b549098ba7', + API_URL: process.env.API_URL || 'https://api.stage.mpdx.org/graphql', + SITE_URL: SiteUrl, + CLIENT_ID: process.env.CLIENT_ID || '4027334344069527005', + CLIENT_SECRET: + process.env.CLIENT_SECRET || + 'V3WBTfLMgXBuL6XNTPm13CIK7Cwvtb0VnQpeQH-Oojx6kuzaD7durA', + BEACON_TOKEN: + process.env.BEACON_TOKEN || '01b4f5f0-7fff-492a-b5ec-d536f3657d10', + }, + }, ]); diff --git a/onesky/download.ts b/onesky/download.ts index 6750cc028f..f8d972a0f2 100644 --- a/onesky/download.ts +++ b/onesky/download.ts @@ -3,46 +3,60 @@ const fs = require('fs'); const onesky = require('@brainly/onesky-utils'); const options = { - secret: process.env.ONESKY_API_SECRET, - apiKey: process.env.ONESKY_API_KEY, - projectId: '367075', + secret: process.env.ONESKY_API_SECRET, + apiKey: process.env.ONESKY_API_KEY, + projectId: '367075', }; interface OneSkyLanguage { - code: string; - translation_progress: string; + code: string; + translation_progress: string; } interface OneSkyFile { - file_name: string; - string_count: number; + file_name: string; + string_count: number; } const getLanguages = async (): Promise => { - const { data: languages }: { data: OneSkyLanguage[] } = JSON.parse(await onesky.getLanguages(options)); - return languages.filter(({ code, translation_progress }) => parseFloat(translation_progress) > 0 && code !== 'en'); + const { data: languages }: { data: OneSkyLanguage[] } = JSON.parse( + await onesky.getLanguages(options), + ); + return languages.filter( + ({ code, translation_progress }) => + parseFloat(translation_progress) > 0 && code !== 'en', + ); }; const getFiles = async (): Promise => { - const { data: files }: { data: OneSkyFile[] } = JSON.parse(await onesky.getFiles(options)); - return files.filter(({ string_count, file_name }) => string_count > 0 && file_name !== 'Manually input'); + const { data: files }: { data: OneSkyFile[] } = JSON.parse( + await onesky.getFiles(options), + ); + return files.filter( + ({ string_count, file_name }) => + string_count > 0 && file_name !== 'Manually input', + ); }; const store = async (language: string, fileName: string): Promise => { - const content = await onesky.getFile({ ...options, language, fileName }); - if (!fs.existsSync(`public/locales/${language}`)) await fs.promises.mkdir(`public/locales/${language}`); - await fs.promises.writeFile(`public/locales/${language}/${fileName}`, content); + const content = await onesky.getFile({ ...options, language, fileName }); + if (!fs.existsSync(`public/locales/${language}`)) + await fs.promises.mkdir(`public/locales/${language}`); + await fs.promises.writeFile( + `public/locales/${language}/${fileName}`, + content, + ); }; const download = async (): Promise => { - const languages = await getLanguages(); - const files = await getFiles(); - console.log(languages, files); - languages.forEach(({ code }) => { - files.forEach(({ file_name }) => { - store(code, file_name); - }); + const languages = await getLanguages(); + const files = await getFiles(); + console.log(languages, files); + languages.forEach(({ code }) => { + files.forEach(({ file_name }) => { + store(code, file_name); }); + }); }; download(); diff --git a/onesky/tsconfig.json b/onesky/tsconfig.json index e94bc3e9ad..3b3f8cedad 100644 --- a/onesky/tsconfig.json +++ b/onesky/tsconfig.json @@ -1,6 +1,6 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "isolatedModules": false - } + "extends": "../tsconfig.json", + "compilerOptions": { + "isolatedModules": false + } } diff --git a/onesky/upload.ts b/onesky/upload.ts index 99e6445768..244fabe324 100644 --- a/onesky/upload.ts +++ b/onesky/upload.ts @@ -3,20 +3,20 @@ const glob = require('glob'); const onesky = require('@brainly/onesky-utils'); const options = { - secret: process.env.ONESKY_API_SECRET, - apiKey: process.env.ONESKY_API_KEY, - projectId: '367075', - format: 'HIERARCHICAL_JSON', - keepStrings: false, - language: 'en', + secret: process.env.ONESKY_API_SECRET, + apiKey: process.env.ONESKY_API_KEY, + projectId: '367075', + format: 'HIERARCHICAL_JSON', + keepStrings: false, + language: 'en', }; glob('public/locales/en/*.json', (_er, paths) => { - paths.forEach((path: string) => { - const content = fs.readFileSync(path, 'utf8').toString(); - const fileName = path.split('/').pop(); - onesky.postFile({ ...options, content, fileName }); - }); + paths.forEach((path: string) => { + const content = fs.readFileSync(path, 'utf8').toString(); + const fileName = path.split('/').pop(); + onesky.postFile({ ...options, content, fileName }); + }); }); export {}; diff --git a/package.json b/package.json index d696b77ec0..d96f1e6539 100644 --- a/package.json +++ b/package.json @@ -1,133 +1,135 @@ { - "name": "mpdx-react", - "version": "0.1.0", - "private": true, - "scripts": { - "start": "next dev", - "build": "next build", - "serve": "next start", - "lint": "tsc && eslint '*/**/*.{js,ts,tsx}' --quiet --fix", - "lint:ci": "tsc && eslint '*/**/*.{js,ts,tsx}'", - "storybook": "start-storybook -p 6006 -c .storybook -s ./public", - "build-storybook": "build-storybook -c .storybook -s ./public", - "test": "cross-env NODE_ICU_DATA=$(yarn bin node-full-icu-path) jest --silent", - "test:watch": "cross-env NODE_ICU_DATA=$(yarn bin node-full-icu-path) jest --watch", - "test:coverage": "cross-env NODE_ICU_DATA=$(yarn bin node-full-icu-path) jest --coverage", - "chromatic": "chromatic", - "extract": "i18next --config i18next-parser.config.js" - }, - "dependencies": { - "@apollo/client": "^3.3.9", - "@apollo/link-context": "^2.0.0-beta.3", - "@babel/runtime": "^7.12.13", - "@date-io/core": "^1.3.13", - "@date-io/date-fns": "^1.3.13", - "@material-ui/core": "^4.11.3", - "@material-ui/icons": "^4.11.2", - "@material-ui/lab": "^4.0.0-alpha.57", - "@material-ui/pickers": "^3.2.10", - "apollo-cache-persist": "^0.1.1", - "axios": "^0.21.1", - "clsx": "^1.1.1", - "date-fns": "^2.17.0", - "formik": "^2.2.6", - "framer-motion": "^3.3.0", - "graphql": "^15.5.0", - "graphql-tag": "^2.12.0", - "i18next": "^19.8.7", - "i18next-browser-languagedetector": "^6.0.1", - "i18next-http-backend": "^1.1.0", - "imagemin-svgo": "^8.0.0", - "isomorphic-fetch": "^3.0.0", - "lodash": "^4.17.20", - "moment": "^2.29.1", - "mui-datatables": "^3.7.6", - "next": "^10.0.6", - "next-auth": "^3.4.1", - "next-compose-plugins": "^2.2.1", - "next-optimized-images": "^2.6.2", - "next-plugin-graphql": "0.0.2", - "next-pwa": "^5.0.5", - "notistack": "^1.0.3", - "query-string": "^6.14.0", - "react": "17.0.1", - "react-dom": "17.0.1", - "react-i18next": "^11.8.6", - "recharts": "^2.0.6", - "uuid": "^8.3.2", - "yup": "^0.32.8" - }, - "devDependencies": { - "@babel/core": "^7.12.16", - "@brainly/onesky-utils": "^1.4.1", - "@next/bundle-analyzer": "^10.0.6", - "@react-dnd/invariant": "^2.0.0", - "@storybook/addon-actions": "^6.1.17", - "@storybook/addon-knobs": "^6.1.17", - "@storybook/addon-viewport": "^6.1.17", - "@storybook/react": "^6.1.17", - "@testing-library/dom": "^7.29.4", - "@testing-library/jest-dom": "^5.11.9", - "@testing-library/react": "^11.2.5", - "@testing-library/user-event": "^12.7.0", - "@types/axios": "^0.14.0", - "@types/css-mediaquery": "^0.1.0", - "@types/faker": "^5.1.6", - "@types/jest": "^26.0.20", - "@types/lodash": "^4.14.168", - "@types/mui-datatables": "^3.7.0", - "@types/next-auth": "^3.1.24", - "@types/node": "^14.14.26", - "@types/query-string": "^6.3.0", - "@types/react": "^17.0.1", - "@types/testing-library__jest-dom": "^5.9.5", - "@types/uuid": "^8.3.0", - "@types/yup": "^0.29.11", - "@typescript-eslint/eslint-plugin": "^4.15.0", - "@typescript-eslint/parser": "^4.15.0", - "babel-loader": "^8.2.2", - "babel-preset-react-app": "^10.0.0", - "chromatic": "^5.6.2", - "core-js": "^3.8.3", - "cross-env": "^7.0.3", - "css-mediaquery": "^0.1.2", - "eslint": "^7.19.0", - "eslint-config-prettier": "^7.2.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jest": "^24.1.3", - "eslint-plugin-prettier": "^3.3.1", - "eslint-plugin-react": "^7.22.0", - "file-loader": "^6.2.0", - "full-icu": "^1.3.1", - "glob": "^7.1.6", - "husky": "^5.0.9", - "i18next-parser": "^3.6.0", - "imagemin": "^7.0.1", - "img-loader": "3.0.2", - "jest": "^26.6.3", - "jest-environment-jsdom-sixteen": "^1.0.3", - "jest-transform-graphql": "^2.1.0", - "lint-staged": "^10.5.4", - "mockdate": "^3.0.2", - "node-mocks-http": "^1.10.1", - "prettier": "^2.2.1", - "prop-types": "^15.7.2", - "regenerator-runtime": "^0.13.7", - "storybook-addon-i18next": "^1.3.0", - "typescript": "^4.1.5", - "url-loader": "4.1.1", - "webpack": "^4.46.0" - }, - "husky": { - "hooks": { - "pre-commit": "tsc --noEmit && lint-staged" - } - }, - "lint-staged": { - "*.{js,ts,tsx}": [ - "eslint --fix", - "git add" - ] + "name": "mpdx-react", + "version": "0.1.0", + "private": true, + "scripts": { + "start": "next dev", + "build": "next build", + "serve": "next start", + "lint": "tsc && eslint '*/**/*.{js,ts,tsx}' --quiet --fix", + "lint:ci": "tsc && eslint '*/**/*.{js,ts,tsx}'", + "storybook": "start-storybook -p 6006 -c .storybook -s ./public", + "build-storybook": "build-storybook -c .storybook -s ./public", + "test": "cross-env NODE_ICU_DATA=$(yarn bin node-full-icu-path) jest --silent", + "test:watch": "cross-env NODE_ICU_DATA=$(yarn bin node-full-icu-path) jest --watch", + "test:coverage": "cross-env NODE_ICU_DATA=$(yarn bin node-full-icu-path) jest --coverage", + "chromatic": "chromatic", + "extract": "i18next --config i18next-parser.config.js", + "prettier:check": "prettier . --list-different", + "prettier:write": "prettier . --write" + }, + "dependencies": { + "@apollo/client": "^3.3.9", + "@apollo/link-context": "^2.0.0-beta.3", + "@babel/runtime": "^7.12.13", + "@date-io/core": "^1.3.13", + "@date-io/date-fns": "^1.3.13", + "@material-ui/core": "^4.11.3", + "@material-ui/icons": "^4.11.2", + "@material-ui/lab": "^4.0.0-alpha.57", + "@material-ui/pickers": "^3.2.10", + "apollo-cache-persist": "^0.1.1", + "axios": "^0.21.1", + "clsx": "^1.1.1", + "date-fns": "^2.17.0", + "formik": "^2.2.6", + "framer-motion": "^3.3.0", + "graphql": "^15.5.0", + "graphql-tag": "^2.12.0", + "i18next": "^19.8.7", + "i18next-browser-languagedetector": "^6.0.1", + "i18next-http-backend": "^1.1.0", + "imagemin-svgo": "^8.0.0", + "isomorphic-fetch": "^3.0.0", + "lodash": "^4.17.20", + "moment": "^2.29.1", + "mui-datatables": "^3.7.6", + "next": "^10.0.6", + "next-auth": "^3.4.1", + "next-compose-plugins": "^2.2.1", + "next-optimized-images": "^2.6.2", + "next-plugin-graphql": "0.0.2", + "next-pwa": "^5.0.5", + "notistack": "^1.0.3", + "query-string": "^6.14.0", + "react": "17.0.1", + "react-dom": "17.0.1", + "react-i18next": "^11.8.6", + "recharts": "^2.0.6", + "uuid": "^8.3.2", + "yup": "^0.32.8" + }, + "devDependencies": { + "@babel/core": "^7.12.16", + "@brainly/onesky-utils": "^1.4.1", + "@next/bundle-analyzer": "^10.0.6", + "@react-dnd/invariant": "^2.0.0", + "@storybook/addon-actions": "^6.1.17", + "@storybook/addon-knobs": "^6.1.17", + "@storybook/addon-viewport": "^6.1.17", + "@storybook/react": "^6.1.17", + "@testing-library/dom": "^7.29.4", + "@testing-library/jest-dom": "^5.11.9", + "@testing-library/react": "^11.2.5", + "@testing-library/user-event": "^12.7.0", + "@types/axios": "^0.14.0", + "@types/css-mediaquery": "^0.1.0", + "@types/faker": "^5.1.6", + "@types/jest": "^26.0.20", + "@types/lodash": "^4.14.168", + "@types/mui-datatables": "^3.7.0", + "@types/next-auth": "^3.1.24", + "@types/node": "^14.14.26", + "@types/query-string": "^6.3.0", + "@types/react": "^17.0.1", + "@types/testing-library__jest-dom": "^5.9.5", + "@types/uuid": "^8.3.0", + "@types/yup": "^0.29.11", + "@typescript-eslint/eslint-plugin": "^4.15.0", + "@typescript-eslint/parser": "^4.15.0", + "babel-loader": "^8.2.2", + "babel-preset-react-app": "^10.0.0", + "chromatic": "^5.6.2", + "core-js": "^3.8.3", + "cross-env": "^7.0.3", + "css-mediaquery": "^0.1.2", + "eslint": "^7.19.0", + "eslint-config-prettier": "^7.2.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jest": "^24.1.3", + "eslint-plugin-prettier": "^3.3.1", + "eslint-plugin-react": "^7.22.0", + "file-loader": "^6.2.0", + "full-icu": "^1.3.1", + "glob": "^7.1.6", + "husky": "^5.0.9", + "i18next-parser": "^3.6.0", + "imagemin": "^7.0.1", + "img-loader": "3.0.2", + "jest": "^26.6.3", + "jest-environment-jsdom-sixteen": "^1.0.3", + "jest-transform-graphql": "^2.1.0", + "lint-staged": "^10.5.4", + "mockdate": "^3.0.2", + "node-mocks-http": "^1.10.1", + "prettier": "^2.2.1", + "prop-types": "^15.7.2", + "regenerator-runtime": "^0.13.7", + "storybook-addon-i18next": "^1.3.0", + "typescript": "^4.1.5", + "url-loader": "4.1.1", + "webpack": "^4.46.0" + }, + "husky": { + "hooks": { + "pre-commit": "tsc --noEmit && lint-staged" } + }, + "lint-staged": { + "*.{js,ts,tsx}": [ + "eslint --fix", + "git add" + ] + } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 99848aeb9a..cb5843df26 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -19,71 +19,92 @@ import i18n from '../src/lib/i18n'; import { AppProvider } from '../src/components/App'; const handleExitComplete = (): void => { - if (typeof window !== 'undefined') { - window.scrollTo({ top: 0 }); - } + if (typeof window !== 'undefined') { + window.scrollTo({ top: 0 }); + } }; export type PageWithLayout = NextPage & { - layout?: ({ children }) => ReactElement; + layout?: ({ children }) => ReactElement; }; const App = ({ Component, pageProps, router }: AppProps): ReactElement => { - const { session } = pageProps; - const Layout = (Component as PageWithLayout).layout || PrimaryLayout; + const { session } = pageProps; + const Layout = (Component as PageWithLayout).layout || PrimaryLayout; - // useEffect(() => { - // // Remove the server-side injected CSS. - // const jssStyles = document.querySelector('#jss-server-side'); - // if (jssStyles) { - // jssStyles.parentElement.removeChild(jssStyles); - // } - // }, []); + // useEffect(() => { + // // Remove the server-side injected CSS. + // const jssStyles = document.querySelector('#jss-server-side'); + // if (jssStyles) { + // jssStyles.parentElement.removeChild(jssStyles); + // } + // }, []); - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; export default App; diff --git a/pages/_document.tsx b/pages/_document.tsx index c4278d6672..d1b9f8db4a 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -1,64 +1,73 @@ import React, { ReactElement } from 'react'; -import Document, { Head, Main, NextScript, DocumentInitialProps } from 'next/document'; +import Document, { + Head, + Main, + NextScript, + DocumentInitialProps, +} from 'next/document'; import { ServerStyleSheets } from '@material-ui/core/styles'; import { RenderPageResult } from 'next/dist/next-server/lib/utils'; import theme from '../src/theme'; class MyDocument extends Document { - render(): ReactElement { - return ( - - - - - -
- - - - ); - } + render(): ReactElement { + return ( + + + + + +
+ + + + ); + } } MyDocument.getInitialProps = async (ctx): Promise => { - // Resolution order - // - // On the server: - // 1. app.getInitialProps - // 2. page.getInitialProps - // 3. document.getInitialProps - // 4. app.render - // 5. page.render - // 6. document.render - // - // On the server with error: - // 1. document.getInitialProps - // 2. app.render - // 3. page.render - // 4. document.render - // - // On the client - // 1. app.getInitialProps - // 2. page.getInitialProps - // 3. app.render - // 4. page.render + // Resolution order + // + // On the server: + // 1. app.getInitialProps + // 2. page.getInitialProps + // 3. document.getInitialProps + // 4. app.render + // 5. page.render + // 6. document.render + // + // On the server with error: + // 1. document.getInitialProps + // 2. app.render + // 3. page.render + // 4. document.render + // + // On the client + // 1. app.getInitialProps + // 2. page.getInitialProps + // 3. app.render + // 4. page.render - // Render app and page and get the context of the page with collected side effects. - const sheets = new ServerStyleSheets(); - const originalRenderPage = ctx.renderPage; + // Render app and page and get the context of the page with collected side effects. + const sheets = new ServerStyleSheets(); + const originalRenderPage = ctx.renderPage; - ctx.renderPage = (): RenderPageResult | Promise => - originalRenderPage({ - enhanceApp: (App) => (props): ReactElement => sheets.collect(), - }); + ctx.renderPage = (): RenderPageResult | Promise => + originalRenderPage({ + enhanceApp: (App) => (props): ReactElement => + sheets.collect(), + }); - const initialProps = await Document.getInitialProps(ctx); + const initialProps = await Document.getInitialProps(ctx); - return { - ...initialProps, - // Styles fragment is rendered after the app and page rendering finish. - styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()], - }; + return { + ...initialProps, + // Styles fragment is rendered after the app and page rendering finish. + styles: [ + ...React.Children.toArray(initialProps.styles), + sheets.getStyleElement(), + ], + }; }; export default MyDocument; diff --git a/pages/accountLists.tsx b/pages/accountLists.tsx index 800519a3fc..6fd75ed5d6 100644 --- a/pages/accountLists.tsx +++ b/pages/accountLists.tsx @@ -11,68 +11,75 @@ import BaseLayout from '../src/components/Layouts/Basic'; import { useApp } from '../src/components/App'; export const GET_ACCOUNT_LISTS_QUERY = gql` - query GetAccountListsQuery { - accountLists { - nodes { - id - name - monthlyGoal - receivedPledges - totalPledges - currency - } - } + query GetAccountListsQuery { + accountLists { + nodes { + id + name + monthlyGoal + receivedPledges + totalPledges + currency + } } + } `; interface Props { - data: GetAccountListsQuery; + data: GetAccountListsQuery; } const AccountListsPage = ({ data }: Props): ReactElement => { - const { dispatch } = useApp(); - const { t } = useTranslation(); + const { dispatch } = useApp(); + const { t } = useTranslation(); - useEffect(() => { - dispatch({ type: 'updateBreadcrumb', breadcrumb: t('Dashboard') }); - }, []); + useEffect(() => { + dispatch({ type: 'updateBreadcrumb', breadcrumb: t('Dashboard') }); + }, []); - return ( - <> - - MPDX | {t('Account Lists')} - - - - ); + return ( + <> + + MPDX | {t('Account Lists')} + + + + ); }; AccountListsPage.layout = BaseLayout; export const getServerSideProps: GetServerSideProps = async ({ - res, - req, + res, + req, }): Promise> => { - const session = await getSession({ req }); + const session = await getSession({ req }); - if (!session?.user['token']) { - res.writeHead(302, { Location: '/' }); - res.end(); - return { props: {} }; - } + if (!session?.user['token']) { + res.writeHead(302, { Location: '/' }); + res.end(); + return { props: {} }; + } - const client = await ssrClient(session?.user['token']); - const response = await client.query({ query: GET_ACCOUNT_LISTS_QUERY }); + const client = await ssrClient(session?.user['token']); + const response = await client.query({ + query: GET_ACCOUNT_LISTS_QUERY, + }); - if (response.data.accountLists.nodes && response.data.accountLists.nodes.length == 1) { - res.writeHead(302, { Location: `/accountLists/${response.data.accountLists.nodes[0].id}` }); - res.end(); - return { props: {} }; - } + if ( + response.data.accountLists.nodes && + response.data.accountLists.nodes.length == 1 + ) { + res.writeHead(302, { + Location: `/accountLists/${response.data.accountLists.nodes[0].id}`, + }); + res.end(); + return { props: {} }; + } - return { - props: { data: response.data }, - }; + return { + props: { data: response.data }, + }; }; export default AccountListsPage; diff --git a/pages/accountLists/[accountListId].tsx b/pages/accountLists/[accountListId].tsx index 150af09c83..0382e3b400 100644 --- a/pages/accountLists/[accountListId].tsx +++ b/pages/accountLists/[accountListId].tsx @@ -11,83 +11,90 @@ import { ssrClient } from '../../src/lib/client'; import { useApp } from '../../src/components/App'; export const GET_DASHBOARD_QUERY = gql` - query GetDashboardQuery($accountListId: ID!) { - user { - firstName - } - accountList(id: $accountListId) { - name - monthlyGoal - receivedPledges - totalPledges - currency - balance - } - reportsDonationHistories(accountListId: $accountListId) { - averageIgnoreCurrent - periods { - startDate - convertedTotal - totals { - currency - convertedAmount - } - } + query GetDashboardQuery($accountListId: ID!) { + user { + firstName + } + accountList(id: $accountListId) { + name + monthlyGoal + receivedPledges + totalPledges + currency + balance + } + reportsDonationHistories(accountListId: $accountListId) { + averageIgnoreCurrent + periods { + startDate + convertedTotal + totals { + currency + convertedAmount } + } } + } `; interface Props { - data: GetDashboardQuery; - accountListId: string; + data: GetDashboardQuery; + accountListId: string; } const AccountListIdPage = ({ data, accountListId }: Props): ReactElement => { - const { dispatch } = useApp(); - const { t } = useTranslation(); + const { dispatch } = useApp(); + const { t } = useTranslation(); - useEffect(() => { - dispatch({ type: 'updateBreadcrumb', breadcrumb: t('Dashboard') }); - dispatch({ type: 'updateAccountListId', accountListId }); - }, []); + useEffect(() => { + dispatch({ type: 'updateBreadcrumb', breadcrumb: t('Dashboard') }); + dispatch({ type: 'updateAccountListId', accountListId }); + }, []); - return ( - <> - - MPDX | {data.accountList.name} - - - - ); + return ( + <> + + MPDX | {data.accountList.name} + + + + ); }; export const getServerSideProps: GetServerSideProps = async ({ - params, - req, - res, + params, + req, + res, }): Promise> => { - const session = await getSession({ req }); + const session = await getSession({ req }); - if (!session?.user['token']) { - res.writeHead(302, { Location: '/' }); - res.end(); - return { props: {} }; - } + if (!session?.user['token']) { + res.writeHead(302, { Location: '/' }); + res.end(); + return { props: {} }; + } - const client = await ssrClient(session?.user['token']); - const response = await client.query({ - query: GET_DASHBOARD_QUERY, - variables: { - accountListId: params.accountListId, - endOfDay: moment().endOf('day').toISOString(), - today: moment().endOf('day').toISOString().slice(0, 10), - twoWeeksFromNow: moment().endOf('day').add(2, 'weeks').toISOString().slice(0, 10), - }, - }); + const client = await ssrClient(session?.user['token']); + const response = await client.query({ + query: GET_DASHBOARD_QUERY, + variables: { + accountListId: params.accountListId, + endOfDay: moment().endOf('day').toISOString(), + today: moment().endOf('day').toISOString().slice(0, 10), + twoWeeksFromNow: moment() + .endOf('day') + .add(2, 'weeks') + .toISOString() + .slice(0, 10), + }, + }); - return { - props: { data: response.data, accountListId: params.accountListId.toString() }, - }; + return { + props: { + data: response.data, + accountListId: params.accountListId.toString(), + }, + }; }; export default AccountListIdPage; diff --git a/pages/accountLists/[accountListId]/tasks.tsx b/pages/accountLists/[accountListId]/tasks.tsx index 7577c056b6..2a4a7aa629 100644 --- a/pages/accountLists/[accountListId]/tasks.tsx +++ b/pages/accountLists/[accountListId]/tasks.tsx @@ -12,81 +12,92 @@ import { TaskFilter } from '../../../src/components/Task/List/List'; import reduceObject from '../../../src/lib/reduceObject'; export const initialFilterFromPath = (path: string): TaskFilter => { - let initialFilter = {}; - const queryString = path.split('?')[1]; + let initialFilter = {}; + const queryString = path.split('?')[1]; - if (queryString) { - const filter = parse(queryString); + if (queryString) { + const filter = parse(queryString); - initialFilter = reduceObject( - (result: TaskFilter, value: string | string[], key: string) => { - switch (key) { - case 'completed': - result.completed = value === 'true'; - break; - case 'wildcardSearch': - result.wildcardSearch = value.toString(); - break; - case 'startAt[max]': - if (!result.startAt) result.startAt = {}; - result.startAt.max = value.toString(); - break; - case 'startAt[min]': - if (!result.startAt) result.startAt = {}; - result.startAt.min = value.toString(); - break; - default: - result[key.replace('[]', '')] = castArray(value); - } - return result; - }, - {}, - filter, - ); + initialFilter = reduceObject( + (result: TaskFilter, value: string | string[], key: string) => { + switch (key) { + case 'completed': + result.completed = value === 'true'; + break; + case 'wildcardSearch': + result.wildcardSearch = value.toString(); + break; + case 'startAt[max]': + if (!result.startAt) result.startAt = {}; + result.startAt.max = value.toString(); + break; + case 'startAt[min]': + if (!result.startAt) result.startAt = {}; + result.startAt.min = value.toString(); + break; + default: + result[key.replace('[]', '')] = castArray(value); + } + return result; + }, + {}, + filter, + ); - initialFilter = pick( - ['userIds', 'tags', 'contactIds', 'activityType', 'completed', 'wildcardSearch', 'startAt'], - initialFilter, - ); + initialFilter = pick( + [ + 'userIds', + 'tags', + 'contactIds', + 'activityType', + 'completed', + 'wildcardSearch', + 'startAt', + ], + initialFilter, + ); - return initialFilter; - } + return initialFilter; + } }; const TasksPage = (): ReactElement => { - const { dispatch } = useApp(); - const { t } = useTranslation(); - const router = useRouter(); + const { dispatch } = useApp(); + const { t } = useTranslation(); + const router = useRouter(); - useEffect(() => { - dispatch({ type: 'updateBreadcrumb', breadcrumb: t('Tasks') }); - dispatch({ type: 'updateAccountListId', accountListId: router.query.accountListId.toString() }); - }, []); + useEffect(() => { + dispatch({ type: 'updateBreadcrumb', breadcrumb: t('Tasks') }); + dispatch({ + type: 'updateAccountListId', + accountListId: router.query.accountListId.toString(), + }); + }, []); - const initialFilter = initialFilterFromPath(router.asPath); + const initialFilter = initialFilterFromPath(router.asPath); - return ( - <> - - MPDX | {t('Tasks')} - - - - ); + return ( + <> + + MPDX | {t('Tasks')} + + + + ); }; export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); + const session = await getSession({ req }); - if (!session?.user['token']) { - res.writeHead(302, { Location: '/' }); - res.end(); - return { props: {} }; - } + if (!session?.user['token']) { + res.writeHead(302, { Location: '/' }); + res.end(); + return { props: {} }; + } - return { - props: {}, - }; + return { + props: {}, + }; }; export default TasksPage; diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts index e8344fb64b..b9df9ef023 100644 --- a/pages/api/auth/[...nextauth].ts +++ b/pages/api/auth/[...nextauth].ts @@ -3,40 +3,44 @@ import NextAuth from 'next-auth'; import { Profile } from './profile'; const options = { - providers: [ - { - id: 'thekey', - name: 'The Key', - type: 'oauth', - version: '2.0', - scope: 'fullticket', - params: { grant_type: 'authorization_code' }, - accessTokenUrl: 'https://thekey.me/cas/api/oauth/token', - authorizationUrl: 'https://thekey.me/cas/login?response_type=code', - profileUrl: `${process.env.SITE_URL}/api/auth/profile`, - profile: (profile: Profile): Profile => profile, - clientId: process.env.CLIENT_ID, - clientSecret: process.env.CLIENT_SECRET, - state: false, - }, - ], - callbacks: { - session: (session, token): Promise => { - return Promise.resolve({ ...session, user: { ...session.user, token: token.token } }); - }, - jwt: async (token, user, _account, profile): Promise => { - if (user) { - return Promise.resolve({ ...token, token: profile.token }); - } else { - return Promise.resolve(token); - } - }, + providers: [ + { + id: 'thekey', + name: 'The Key', + type: 'oauth', + version: '2.0', + scope: 'fullticket', + params: { grant_type: 'authorization_code' }, + accessTokenUrl: 'https://thekey.me/cas/api/oauth/token', + authorizationUrl: 'https://thekey.me/cas/login?response_type=code', + profileUrl: `${process.env.SITE_URL}/api/auth/profile`, + profile: (profile: Profile): Profile => profile, + clientId: process.env.CLIENT_ID, + clientSecret: process.env.CLIENT_SECRET, + state: false, }, - jwt: { - secret: process.env.JWT_SECRET, + ], + callbacks: { + session: (session, token): Promise => { + return Promise.resolve({ + ...session, + user: { ...session.user, token: token.token }, + }); }, + jwt: async (token, user, _account, profile): Promise => { + if (user) { + return Promise.resolve({ ...token, token: profile.token }); + } else { + return Promise.resolve(token); + } + }, + }, + jwt: { + secret: process.env.JWT_SECRET, + }, }; -const Auth = (req: NextApiRequest, res: NextApiResponse): Promise => NextAuth(req, res, options); +const Auth = (req: NextApiRequest, res: NextApiResponse): Promise => + NextAuth(req, res, options); export default Auth; diff --git a/pages/api/auth/profile.ts b/pages/api/auth/profile.ts index 5379814ab1..83397086a3 100644 --- a/pages/api/auth/profile.ts +++ b/pages/api/auth/profile.ts @@ -4,42 +4,45 @@ import { gql } from '@apollo/client'; import client from '../../../src/lib/client'; export interface Profile { - id: string; - name: string; - token: string; + id: string; + name: string; + token: string; } -const profile = async (req: NextApiRequest, res: NextApiResponse): Promise => { - const { - data: { ticket: ticket }, - } = await Axios.get('https://thekey.me/cas/api/oauth/ticket', { - headers: { - Authorization: req.headers.authorization, - Accept: 'application/json', - }, - params: { - service: process.env.API_URL, - }, - }); +const profile = async ( + req: NextApiRequest, + res: NextApiResponse, +): Promise => { + const { + data: { ticket: ticket }, + } = await Axios.get('https://thekey.me/cas/api/oauth/ticket', { + headers: { + Authorization: req.headers.authorization, + Accept: 'application/json', + }, + params: { + service: process.env.API_URL, + }, + }); - const response = await client.mutate({ - mutation: gql` - mutation UserKeySignIn($ticket: String!) { - userKeySignIn(input: { casTicket: $ticket }) { - token - user { - id - name: firstName - } - } - } - `, - variables: { - ticket, - }, - }); - const { user, token } = response.data.userKeySignIn; - res.status(200).json({ ...user, token }); + const response = await client.mutate({ + mutation: gql` + mutation UserKeySignIn($ticket: String!) { + userKeySignIn(input: { casTicket: $ticket }) { + token + user { + id + name: firstName + } + } + } + `, + variables: { + ticket, + }, + }); + const { user, token } = response.data.userKeySignIn; + res.status(200).json({ ...user, token }); }; export default profile; diff --git a/pages/api/graphql.ts b/pages/api/graphql.ts index d98869cb6b..d62dd436ca 100644 --- a/pages/api/graphql.ts +++ b/pages/api/graphql.ts @@ -2,15 +2,18 @@ import { NextApiRequest, NextApiResponse } from 'next'; import Axios from 'axios'; import jwt from 'next-auth/jwt'; -const graphql = async (req: NextApiRequest, res: NextApiResponse): Promise => { - const jwtToken = await jwt.getToken({ req, secret: process.env.JWT_SECRET }); - const response = await Axios.post(process.env.API_URL, req.body, { - headers: { - Authorization: jwtToken ? `Bearer ${jwtToken['token']}` : null, - Accept: 'application/json', - }, - }); - res.status(200).json(response.data); +const graphql = async ( + req: NextApiRequest, + res: NextApiResponse, +): Promise => { + const jwtToken = await jwt.getToken({ req, secret: process.env.JWT_SECRET }); + const response = await Axios.post(process.env.API_URL, req.body, { + headers: { + Authorization: jwtToken ? `Bearer ${jwtToken['token']}` : null, + Accept: 'application/json', + }, + }); + res.status(200).json(response.data); }; export default graphql; diff --git a/pages/api/handoff.ts b/pages/api/handoff.ts index 217050767d..948bce2e9e 100644 --- a/pages/api/handoff.ts +++ b/pages/api/handoff.ts @@ -1,30 +1,44 @@ import { NextApiRequest, NextApiResponse } from 'next'; import jwt from 'next-auth/jwt'; -const handoff = async (req: NextApiRequest, res: NextApiResponse): Promise => { - const jwtToken = await jwt.getToken({ req, secret: process.env.JWT_SECRET }); - if (jwtToken && req.query.accountListId && req.query.userId && req.query.path && req.query.auth !== 'true') { - const url = new URL( - `https://${process.env.SITE_URL === 'https://next.mpdx.org' ? '' : 'stage.'}mpdx.org/handoff`, - ); +const handoff = async ( + req: NextApiRequest, + res: NextApiResponse, +): Promise => { + const jwtToken = await jwt.getToken({ req, secret: process.env.JWT_SECRET }); + if ( + jwtToken && + req.query.accountListId && + req.query.userId && + req.query.path && + req.query.auth !== 'true' + ) { + const url = new URL( + `https://${ + process.env.SITE_URL === 'https://next.mpdx.org' ? '' : 'stage.' + }mpdx.org/handoff`, + ); - url.searchParams.append('accessToken', jwtToken['token']); - url.searchParams.append('accountListId', req.query.accountListId.toString()); - url.searchParams.append('userId', req.query.userId.toString()); - url.searchParams.append('path', req.query.path.toString()); - res.redirect(url.href); - } else if (jwtToken && req.query.path && req.query.auth === 'true') { - const url = new URL( - `https://auth.${ - process.env.SITE_URL === 'https://next.mpdx.org' ? '' : 'stage.' - }mpdx.org/${req.query.path.toString().replace(/^\/+/, '')}`, - ); + url.searchParams.append('accessToken', jwtToken['token']); + url.searchParams.append( + 'accountListId', + req.query.accountListId.toString(), + ); + url.searchParams.append('userId', req.query.userId.toString()); + url.searchParams.append('path', req.query.path.toString()); + res.redirect(url.href); + } else if (jwtToken && req.query.path && req.query.auth === 'true') { + const url = new URL( + `https://auth.${ + process.env.SITE_URL === 'https://next.mpdx.org' ? '' : 'stage.' + }mpdx.org/${req.query.path.toString().replace(/^\/+/, '')}`, + ); - url.searchParams.append('access_token', jwtToken['token']); - res.redirect(url.href); - } else { - res.status(422); - } + url.searchParams.append('access_token', jwtToken['token']); + res.redirect(url.href); + } else { + res.status(422); + } }; export default handoff; diff --git a/pages/index.tsx b/pages/index.tsx index 5563ea90cb..b2519eea2f 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -7,17 +7,19 @@ const IndexPage = (): ReactElement => <>; IndexPage.layout = BaseLayout; -export const getServerSideProps: GetServerSideProps = async (context): Promise> => { - const session = await getSession(context); +export const getServerSideProps: GetServerSideProps = async ( + context, +): Promise> => { + const session = await getSession(context); - if (session) { - context.res.writeHead(302, { Location: '/accountLists' }); - context.res.end(); - } else { - context.res.writeHead(302, { Location: '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/login' }); - context.res.end(); - } - return { props: {} }; + if (session) { + context.res.writeHead(302, { Location: '/accountLists' }); + context.res.end(); + } else { + context.res.writeHead(302, { Location: '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/login' }); + context.res.end(); + } + return { props: {} }; }; export default IndexPage; diff --git a/pages/login.tsx b/pages/login.tsx index e49390231b..b62f890c95 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -9,48 +9,50 @@ import BaseLayout from '../src/components/Layouts/Basic'; import logo from '../src/images/logo.svg'; const IndexPage = (): ReactElement => ( - <> - - MPDX | Home - - } - subtitle="MPDX is fundraising software from Cru that helps you grow and maintain your ministry + <> + + MPDX | Home + + } + subtitle="MPDX is fundraising software from Cru that helps you grow and maintain your ministry partners in a quick and easy way." - > - - - - + > + + + + ); IndexPage.layout = BaseLayout; -export const getServerSideProps: GetServerSideProps = async (context): Promise> => { - const session = await getSession(context); - - if (context.res && session) { - context.res.writeHead(302, { Location: '/accountLists' }); - context.res.end(); - return { props: {} }; - } +export const getServerSideProps: GetServerSideProps = async ( + context, +): Promise> => { + const session = await getSession(context); + if (context.res && session) { + context.res.writeHead(302, { Location: '/accountLists' }); + context.res.end(); return { props: {} }; + } + + return { props: {} }; }; export default IndexPage; diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 181c699c98..0af78359fd 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -1,208 +1,208 @@ { - "My Accounts": "My Accounts", - "Goal": "Goal", - "Gifts Started": "Gifts Started", - "Committed": "Committed", - "Account Balance": "Account Balance", - "It may take a few days to update.": "It may take a few days to update.", - "View Gifts": "View Gifts", - "Average": "Average", - "No monthly activity to show.": "No monthly activity to show.", - "Amount ({{ currencyCode }})": "Amount ({{ currencyCode }})", - "Monthly Goal": "Monthly Goal", - "Commitments": "Commitments", - "Below Goal": "Below Goal", - "Above Goal": "Above Goal", - "Appeals": "Appeals", - "No primary appeal to show.": "No primary appeal to show.", - "Gifts Received": "Gifts Received", - "View All": "View All", - "Late Commitments": "Late Commitments", - "View All ({{ totalCount, number }})": "View All ({{ totalCount, number }})", - "No late commitments to show.": "No late commitments to show.", - "Their gift is {{ count, number }} day late.": "Their gift is {{ count, number }} day late.", - "Their gift is {{ count, number }} day late._plural": "Their gift is {{ count, number }} days late.", - "Partner Care": "Partner Care", - "Prayer ({{ totalCount, number }})": "Prayer ({{ totalCount, number }})", - "Celebrations ({{ totalCount, number }})": "Celebrations ({{ totalCount, number }})", - "No prayer requests to show.": "No prayer requests to show.", - "No celebrations to show.": "No celebrations to show.", - "No referrals to show.": "No referrals to show.", - "Referrals": "Referrals", - "Recent ({{ totalCount, number }})": "Recent ({{ totalCount, number }})", - "On Hand ({{ totalCount, number }})": "On Hand ({{ totalCount, number }})", - "Tasks Due This Week": "Tasks Due This Week", - "No tasks to show.": "No tasks to show.", - "To Do This Week": "To Do This Week", - "Weekly Activity": "Weekly Activity", - "Completed": "Completed", - "Appt Produced": "Appt Produced", - "Calls": "Calls", - "Messages": "Messages", - "Appointments": "Appointments", - "Correspondence": "Correspondence", - "View Activity Detail": "View Activity Detail", - "Good Evening, {{ firstName }}.": "Good Evening, {{ firstName }}.", - "Good Evening,": "Good Evening,", - "Good Morning, {{ firstName }}.": "Good Morning, {{ firstName }}.", - "Good Morning,": "Good Morning,", - "Good Afternoon, {{ firstName }}.": "Good Afternoon, {{ firstName }}.", - "Good Afternoon,": "Good Afternoon,", - "Welcome back to MPDX. Here's what's been happening.": "Welcome back to MPDX. Here's what's been happening.", - "Privacy Policy": "Privacy Policy", - "What's New": "What's New", - "Terms of Use": "Terms of Use", - "© {{ year }}, Cru. All Rights Reserved.": "© {{ year }}, Cru. All Rights Reserved.", - "Overview": "Overview", - "Dashboard": "Dashboard", - "Contacts": "Contacts", - "Tasks": "Tasks", - "Reports": "Reports", - "Gifts": "Gifts", - "14 Month Report": "14 Month Report", - "Designation Accounts": "Designation Accounts", - "Responsibility Centers": "Responsibility Centers", - "Expected Monthly Total": "Expected Monthly Total", - "Partner Giving Analysis": "Partner Giving Analysis", - "Coaching": "Coaching", - "Tools": "Tools", - "Import from Google": "Import from Google", - "Import from CSV": "Import from CSV", - "Import from TntConnect": "Import from TntConnect", - "Fix Commitment Info": "Fix Commitment Info", - "Fix Email Addresses": "Fix Email Addresses", - "Fix Mailing Addresses": "Fix Mailing Addresses", - "Fix Phone Numbers": "Fix Phone Numbers", - "Fix Send Newsletter": "Fix Send Newsletter", - "Merge Contacts": "Merge Contacts", - "Merge People": "Merge People", - "No call logged in the past year": "No call logged in the past year", - "Gave a gift of {{ amount, currency }} which is greater than their commitment amount": "Gave a gift of {{ amount, currency }} which is greater than their commitment amount", - "Gave a larger gift than their commitment amount": "Gave a larger gift than their commitment amount", - "Gave a gift of {{ amount, currency }} where commitment frequency is set to semi-annual or greater": "Gave a gift of {{ amount, currency }} where commitment frequency is set to semi-annual or greater", - "Gave a gift where commitment frequency is set to semi-annual or greater": "Gave a gift where commitment frequency is set to semi-annual or greater", - "On your physical newsletter list but has no mailing address": "On your physical newsletter list but has no mailing address", - "On your email newsletter list but has no people with a valid email address": "On your email newsletter list but has no people with a valid email address", - "Added through your Give Site subscription form": "Added through your Give Site subscription form", - "Added and merged": "Added and merged", - "Added but not merged": "Added but not merged", - "Added with no duplicate found": "Added with no duplicate found", - "Recontinued giving": "Recontinued giving", - "Semi-annual or greater gift is expected one month from now": "Semi-annual or greater gift is expected one month from now", - "Gave a gift of {{ amount, currency }} which is less than their commitment amount": "Gave a gift of {{ amount, currency }} which is less than their commitment amount", - "Gave a smaller gift than their commitment amount": "Gave a smaller gift than their commitment amount", - "Gave a special gift of {{ amount, currency }}": "Gave a special gift of {{ amount, currency }}", - "Gave a special gift": "Gave a special gift", - "Started giving": "Started giving", - "Missed a gift": "Missed a gift", - "No thank you note logged in the past year": "No thank you note logged in the past year", - "Upcoming anniversary": "Upcoming anniversary", - "Upcoming birthday": "Upcoming birthday", - "Notifications": "Notifications", - "Mark all as read": "Mark all as read", - "Load More": "Load More", - "No notifications to show.": "No notifications to show.", - "See All Account Lists": "See All Account Lists", - "Go to help": "Go to help", - "Preferences": "Preferences", - "Connect Services": "Connect Services", - "Manage Accounts": "Manage Accounts", - "Manage Coaches": "Manage Coaches", - "Sign Out": "Sign Out", - "No Comments to show.": "No Comments to show.", - "Task saved successfully": "Task saved successfully", - "Result": "Result", - "Next Action": "Next Action", - "Completed Date": "Completed Date", - "OK": "OK", - "Today": "Today", - "Cancel": "Cancel", - "Clear": "Clear", - "Completed Time": "Completed Time", - "Tags": "Tags", - "Save": "Save", - "No Contacts to show.": "No Contacts to show.", - "Newsletter": "Newsletter", - "Commitment": "Commitment", - "Last Gift": "Last Gift", - "Task": "Task", - "Description": "Description", - "Action": "Action", - "APPOINTMENT": "Appointment", - "CALL": "Call", - "EMAIL": "Email", - "FACEBOOK_MESSAGE": "Facebook Message", - "LETTER": "Letter", - "NEWSLETTER_EMAIL": "Newsletter - Email", - "NEWSLETTER_PHYSICAL": "Newsletter - Physical", - "NONE": "None", - "PRAYER_REQUEST": "Prayer Request", - "PRE_CALL_LETTER": "Pre-Call Letter", - "REMINDER_LETTER": "Reminder Letter", - "SUPPORT_LETTER": "Support Letter", - "TALK_TO_IN_PERSON": "Talk To In Person", - "TEXT_MESSAGE": "Text Message", - "THANK": "Thank", - "TO_DO": "To Do", - "DAYS": "Days", - "HOURS": "Hours", - "MINUTES": "Minutes", - "BOTH": "Both", - "MOBILE": "Mobile", - "APPOINTMENT_SCHEDULED": "Appointment Scheduled", - "ASK_IN_FUTURE": "Ask In Future", - "CALL_FOR_DECISION": "Call For Decision", - "CONTACT_FOR_APPOINTMENT": "Contact For Appointment", - "CULTIVATE_RELATIONSHIP": "Cultivate Relationship", - "EXPIRED_REFERRAL": "Expired Referral", - "NEVER_ASK": "Never Ask", - "NEVER_CONTACTED": "Never Contacted", - "NOT_INTERESTED": "Not Interested", - "PARTNER_FINANCIAL": "Partner - Financial", - "PARTNER_PRAY": "Partner - Pray", - "PARTNER_SPECIAL": "Partner - Special", - "RESEARCH_ABANDONED": "Research Abandoned", - "UNRESPONSIVE": "Unresponsive", - "List Separator": ", ", - "PHYSICAL": "Physical", - "ANNUAL": "Annual", - "EVERY_2_MONTHS": "Every 2 Months", - "EVERY_2_WEEKS": "Every 2 Weeks", - "EVERY_2_YEARS": "Every 2 Years", - "EVERY_4_MONTHS": "Every 4 Months", - "EVERY_6_MONTHS": "Every 6 Months", - "MONTHLY": "Monthly", - "QUARTERLY": "Quarterly", - "WEEKLY": "Weekly", - "ATTEMPTED": "Attempted", - "ATTEMPTED_LEFT_MESSAGE": "Attempted - Left Message", - "COMPLETED": "Completed", - "DONE": "Done", - "RECEIVED": "Received", - "Complete {{activityType}}": "Complete {{activityType}}", - "Add Task": "Add Task", - "Details": "Details", - "Contacts ({{ contactCount }})": "Contacts ({{ contactCount }})", - "Comments": "Comments", - "Subject": "Subject", - "Field is required": "Field is required", - "Type": "Type", - "None": "None", - "Due Date": "Due Date", - "Due Time": "Due Time", - "Assignee": "Assignee", - "Notification": "Notification", - "Period": "Period", - "Unit": "Unit", - "Platform": "Platform", - "Complete": "Complete", - "Incomplete": "Incomplete", - "Loading": "Loading", - "Tag {{tag}}": "Tag: {{tag}}", - "Due Date {{ minimumDate }} - {{ maximumDate }}": "Due Date: {{ minimumDate }} - {{ maximumDate }}", - "Minimum Due Date {{ minimumDate }}": "Minimum Due Date: {{ minimumDate }}", - "Maximum Due Date {{ maximumDate }}": "Maximum Due Date: {{ maximumDate }}", - "Minimum": "Minimum", - "Maximum": "Maximum", - "No Due Date": "No Due Date" + "My Accounts": "My Accounts", + "Goal": "Goal", + "Gifts Started": "Gifts Started", + "Committed": "Committed", + "Account Balance": "Account Balance", + "It may take a few days to update.": "It may take a few days to update.", + "View Gifts": "View Gifts", + "Average": "Average", + "No monthly activity to show.": "No monthly activity to show.", + "Amount ({{ currencyCode }})": "Amount ({{ currencyCode }})", + "Monthly Goal": "Monthly Goal", + "Commitments": "Commitments", + "Below Goal": "Below Goal", + "Above Goal": "Above Goal", + "Appeals": "Appeals", + "No primary appeal to show.": "No primary appeal to show.", + "Gifts Received": "Gifts Received", + "View All": "View All", + "Late Commitments": "Late Commitments", + "View All ({{ totalCount, number }})": "View All ({{ totalCount, number }})", + "No late commitments to show.": "No late commitments to show.", + "Their gift is {{ count, number }} day late.": "Their gift is {{ count, number }} day late.", + "Their gift is {{ count, number }} day late._plural": "Their gift is {{ count, number }} days late.", + "Partner Care": "Partner Care", + "Prayer ({{ totalCount, number }})": "Prayer ({{ totalCount, number }})", + "Celebrations ({{ totalCount, number }})": "Celebrations ({{ totalCount, number }})", + "No prayer requests to show.": "No prayer requests to show.", + "No celebrations to show.": "No celebrations to show.", + "No referrals to show.": "No referrals to show.", + "Referrals": "Referrals", + "Recent ({{ totalCount, number }})": "Recent ({{ totalCount, number }})", + "On Hand ({{ totalCount, number }})": "On Hand ({{ totalCount, number }})", + "Tasks Due This Week": "Tasks Due This Week", + "No tasks to show.": "No tasks to show.", + "To Do This Week": "To Do This Week", + "Weekly Activity": "Weekly Activity", + "Completed": "Completed", + "Appt Produced": "Appt Produced", + "Calls": "Calls", + "Messages": "Messages", + "Appointments": "Appointments", + "Correspondence": "Correspondence", + "View Activity Detail": "View Activity Detail", + "Good Evening, {{ firstName }}.": "Good Evening, {{ firstName }}.", + "Good Evening,": "Good Evening,", + "Good Morning, {{ firstName }}.": "Good Morning, {{ firstName }}.", + "Good Morning,": "Good Morning,", + "Good Afternoon, {{ firstName }}.": "Good Afternoon, {{ firstName }}.", + "Good Afternoon,": "Good Afternoon,", + "Welcome back to MPDX. Here's what's been happening.": "Welcome back to MPDX. Here's what's been happening.", + "Privacy Policy": "Privacy Policy", + "What's New": "What's New", + "Terms of Use": "Terms of Use", + "© {{ year }}, Cru. All Rights Reserved.": "© {{ year }}, Cru. All Rights Reserved.", + "Overview": "Overview", + "Dashboard": "Dashboard", + "Contacts": "Contacts", + "Tasks": "Tasks", + "Reports": "Reports", + "Gifts": "Gifts", + "14 Month Report": "14 Month Report", + "Designation Accounts": "Designation Accounts", + "Responsibility Centers": "Responsibility Centers", + "Expected Monthly Total": "Expected Monthly Total", + "Partner Giving Analysis": "Partner Giving Analysis", + "Coaching": "Coaching", + "Tools": "Tools", + "Import from Google": "Import from Google", + "Import from CSV": "Import from CSV", + "Import from TntConnect": "Import from TntConnect", + "Fix Commitment Info": "Fix Commitment Info", + "Fix Email Addresses": "Fix Email Addresses", + "Fix Mailing Addresses": "Fix Mailing Addresses", + "Fix Phone Numbers": "Fix Phone Numbers", + "Fix Send Newsletter": "Fix Send Newsletter", + "Merge Contacts": "Merge Contacts", + "Merge People": "Merge People", + "No call logged in the past year": "No call logged in the past year", + "Gave a gift of {{ amount, currency }} which is greater than their commitment amount": "Gave a gift of {{ amount, currency }} which is greater than their commitment amount", + "Gave a larger gift than their commitment amount": "Gave a larger gift than their commitment amount", + "Gave a gift of {{ amount, currency }} where commitment frequency is set to semi-annual or greater": "Gave a gift of {{ amount, currency }} where commitment frequency is set to semi-annual or greater", + "Gave a gift where commitment frequency is set to semi-annual or greater": "Gave a gift where commitment frequency is set to semi-annual or greater", + "On your physical newsletter list but has no mailing address": "On your physical newsletter list but has no mailing address", + "On your email newsletter list but has no people with a valid email address": "On your email newsletter list but has no people with a valid email address", + "Added through your Give Site subscription form": "Added through your Give Site subscription form", + "Added and merged": "Added and merged", + "Added but not merged": "Added but not merged", + "Added with no duplicate found": "Added with no duplicate found", + "Recontinued giving": "Recontinued giving", + "Semi-annual or greater gift is expected one month from now": "Semi-annual or greater gift is expected one month from now", + "Gave a gift of {{ amount, currency }} which is less than their commitment amount": "Gave a gift of {{ amount, currency }} which is less than their commitment amount", + "Gave a smaller gift than their commitment amount": "Gave a smaller gift than their commitment amount", + "Gave a special gift of {{ amount, currency }}": "Gave a special gift of {{ amount, currency }}", + "Gave a special gift": "Gave a special gift", + "Started giving": "Started giving", + "Missed a gift": "Missed a gift", + "No thank you note logged in the past year": "No thank you note logged in the past year", + "Upcoming anniversary": "Upcoming anniversary", + "Upcoming birthday": "Upcoming birthday", + "Notifications": "Notifications", + "Mark all as read": "Mark all as read", + "Load More": "Load More", + "No notifications to show.": "No notifications to show.", + "See All Account Lists": "See All Account Lists", + "Go to help": "Go to help", + "Preferences": "Preferences", + "Connect Services": "Connect Services", + "Manage Accounts": "Manage Accounts", + "Manage Coaches": "Manage Coaches", + "Sign Out": "Sign Out", + "No Comments to show.": "No Comments to show.", + "Task saved successfully": "Task saved successfully", + "Result": "Result", + "Next Action": "Next Action", + "Completed Date": "Completed Date", + "OK": "OK", + "Today": "Today", + "Cancel": "Cancel", + "Clear": "Clear", + "Completed Time": "Completed Time", + "Tags": "Tags", + "Save": "Save", + "No Contacts to show.": "No Contacts to show.", + "Newsletter": "Newsletter", + "Commitment": "Commitment", + "Last Gift": "Last Gift", + "Task": "Task", + "Description": "Description", + "Action": "Action", + "APPOINTMENT": "Appointment", + "CALL": "Call", + "EMAIL": "Email", + "FACEBOOK_MESSAGE": "Facebook Message", + "LETTER": "Letter", + "NEWSLETTER_EMAIL": "Newsletter - Email", + "NEWSLETTER_PHYSICAL": "Newsletter - Physical", + "NONE": "None", + "PRAYER_REQUEST": "Prayer Request", + "PRE_CALL_LETTER": "Pre-Call Letter", + "REMINDER_LETTER": "Reminder Letter", + "SUPPORT_LETTER": "Support Letter", + "TALK_TO_IN_PERSON": "Talk To In Person", + "TEXT_MESSAGE": "Text Message", + "THANK": "Thank", + "TO_DO": "To Do", + "DAYS": "Days", + "HOURS": "Hours", + "MINUTES": "Minutes", + "BOTH": "Both", + "MOBILE": "Mobile", + "APPOINTMENT_SCHEDULED": "Appointment Scheduled", + "ASK_IN_FUTURE": "Ask In Future", + "CALL_FOR_DECISION": "Call For Decision", + "CONTACT_FOR_APPOINTMENT": "Contact For Appointment", + "CULTIVATE_RELATIONSHIP": "Cultivate Relationship", + "EXPIRED_REFERRAL": "Expired Referral", + "NEVER_ASK": "Never Ask", + "NEVER_CONTACTED": "Never Contacted", + "NOT_INTERESTED": "Not Interested", + "PARTNER_FINANCIAL": "Partner - Financial", + "PARTNER_PRAY": "Partner - Pray", + "PARTNER_SPECIAL": "Partner - Special", + "RESEARCH_ABANDONED": "Research Abandoned", + "UNRESPONSIVE": "Unresponsive", + "List Separator": ", ", + "PHYSICAL": "Physical", + "ANNUAL": "Annual", + "EVERY_2_MONTHS": "Every 2 Months", + "EVERY_2_WEEKS": "Every 2 Weeks", + "EVERY_2_YEARS": "Every 2 Years", + "EVERY_4_MONTHS": "Every 4 Months", + "EVERY_6_MONTHS": "Every 6 Months", + "MONTHLY": "Monthly", + "QUARTERLY": "Quarterly", + "WEEKLY": "Weekly", + "ATTEMPTED": "Attempted", + "ATTEMPTED_LEFT_MESSAGE": "Attempted - Left Message", + "COMPLETED": "Completed", + "DONE": "Done", + "RECEIVED": "Received", + "Complete {{activityType}}": "Complete {{activityType}}", + "Add Task": "Add Task", + "Details": "Details", + "Contacts ({{ contactCount }})": "Contacts ({{ contactCount }})", + "Comments": "Comments", + "Subject": "Subject", + "Field is required": "Field is required", + "Type": "Type", + "None": "None", + "Due Date": "Due Date", + "Due Time": "Due Time", + "Assignee": "Assignee", + "Notification": "Notification", + "Period": "Period", + "Unit": "Unit", + "Platform": "Platform", + "Complete": "Complete", + "Incomplete": "Incomplete", + "Loading": "Loading", + "Tag {{tag}}": "Tag: {{tag}}", + "Due Date {{ minimumDate }} - {{ maximumDate }}": "Due Date: {{ minimumDate }} - {{ maximumDate }}", + "Minimum Due Date {{ minimumDate }}": "Minimum Due Date: {{ minimumDate }}", + "Maximum Due Date {{ maximumDate }}": "Maximum Due Date: {{ maximumDate }}", + "Minimum": "Minimum", + "Maximum": "Maximum", + "No Due Date": "No Due Date" } diff --git a/public/manifest.json b/public/manifest.json index eabef349bb..ac587fe7fe 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,53 +1,53 @@ { - "name": "MPDX", - "theme_color": "#05699b", - "background_color": "#05699b", - "display": "standalone", - "Scope": "/", - "start_url": "/", - "short_name": "MPDX", - "icons": [ - { - "src": "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/icons/icon-72x72.png", - "sizes": "72x72", - "type": "image/png" - }, - { - "src": "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/icons/icon-96x96.png", - "sizes": "96x96", - "type": "image/png" - }, - { - "src": "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/icons/icon-128x128.png", - "sizes": "128x128", - "type": "image/png" - }, - { - "src": "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/icons/icon-144x144.png", - "sizes": "144x144", - "type": "image/png" - }, - { - "src": "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/icons/icon-152x152.png", - "sizes": "152x152", - "type": "image/png" - }, - { - "src": "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/icons/icon-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "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/icons/icon-384x384.png", - "sizes": "384x384", - "type": "image/png" - }, - { - "src": "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/icons/icon-512x512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "any maskable" - } - ], - "splash_pages": null + "name": "MPDX", + "theme_color": "#05699b", + "background_color": "#05699b", + "display": "standalone", + "Scope": "/", + "start_url": "/", + "short_name": "MPDX", + "icons": [ + { + "src": "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/icons/icon-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "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/icons/icon-96x96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "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/icons/icon-128x128.png", + "sizes": "128x128", + "type": "image/png" + }, + { + "src": "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/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "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/icons/icon-152x152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "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/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "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/icons/icon-384x384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "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/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ], + "splash_pages": null } diff --git a/src/components/AccountLists/AccountLists.stories.tsx b/src/components/AccountLists/AccountLists.stories.tsx index f9af781e27..8fed32cfee 100644 --- a/src/components/AccountLists/AccountLists.stories.tsx +++ b/src/components/AccountLists/AccountLists.stories.tsx @@ -2,48 +2,48 @@ import React, { ReactElement } from 'react'; import AccountLists from './AccountLists'; export default { - title: 'AccountLists', + title: 'AccountLists', }; export const Default = (): ReactElement => { - return ( - - ); + return ( + + ); }; Default.story = { - parameters: { - chromatic: { delay: 1000 }, - }, + parameters: { + chromatic: { delay: 1000 }, + }, }; diff --git a/src/components/AccountLists/AccountLists.test.tsx b/src/components/AccountLists/AccountLists.test.tsx index ed9b75e774..4293dd9c1c 100644 --- a/src/components/AccountLists/AccountLists.test.tsx +++ b/src/components/AccountLists/AccountLists.test.tsx @@ -3,43 +3,43 @@ import { render } from '@testing-library/react'; import AccountLists from '.'; describe('AccountLists', () => { - it('has correct defaults', () => { - const { getByTestId } = render( - , - ); - expect(getByTestId('abc')).toHaveTextContent('My Personal Staff Account'); - expect(getByTestId('def')).toHaveTextContent('My Ministry Account'); - expect(getByTestId('ghi')).toHaveTextContent("My Friend's Staff Account"); - }); + it('has correct defaults', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('abc')).toHaveTextContent('My Personal Staff Account'); + expect(getByTestId('def')).toHaveTextContent('My Ministry Account'); + expect(getByTestId('ghi')).toHaveTextContent("My Friend's Staff Account"); + }); }); diff --git a/src/components/AccountLists/AccountLists.tsx b/src/components/AccountLists/AccountLists.tsx index 76deec5714..a56139ccc9 100644 --- a/src/components/AccountLists/AccountLists.tsx +++ b/src/components/AccountLists/AccountLists.tsx @@ -1,5 +1,14 @@ import React, { ReactElement } from 'react'; -import { makeStyles, Theme, Container, Typography, Grid, CardActionArea, CardContent, Box } from '@material-ui/core'; +import { + makeStyles, + Theme, + Container, + Typography, + Grid, + CardActionArea, + CardContent, + Box, +} from '@material-ui/core'; import Link from 'next/link'; import { motion } from 'framer-motion'; import { useTranslation } from 'react-i18next'; @@ -9,117 +18,138 @@ import AnimatedCard from '../AnimatedCard'; import { currencyFormat, percentageFormat } from '../../lib/intlFormat'; interface Props { - data: GetAccountListsQuery; + data: GetAccountListsQuery; } const useStyles = makeStyles((theme: Theme) => ({ - box: { - paddingBottom: theme.spacing(5), - backgroundColor: '#f6f7f9', + box: { + paddingBottom: theme.spacing(5), + backgroundColor: '#f6f7f9', + }, + cardContent: { + display: 'flex', + flexDirection: 'column', + height: '200px', + }, + image: { + '& img': { + height: '160px', }, - cardContent: { - display: 'flex', - flexDirection: 'column', - height: '200px', - }, - image: { - '& img': { - height: '160px', - }, - [theme.breakpoints.down('xs')]: { - display: 'none', - }, + [theme.breakpoints.down('xs')]: { + display: 'none', }, + }, })); const variants = { - animate: { - transition: { - staggerChildren: 0.15, - }, + animate: { + transition: { + staggerChildren: 0.15, }, - exit: { - transition: { - staggerChildren: 0.1, - }, + }, + exit: { + transition: { + staggerChildren: 0.1, }, + }, }; const AccountLists = ({ data }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); + const classes = useStyles(); + const { t } = useTranslation(); - return ( - - - - - - {data.accountLists.nodes.map( - ({ id, name, monthlyGoal, receivedPledges, totalPledges, currency }) => { - const receivedPercentage = receivedPledges / monthlyGoal; - const totalPercentage = totalPledges / monthlyGoal; + return ( + + + + + + {data.accountLists.nodes.map( + ({ + id, + name, + monthlyGoal, + receivedPledges, + totalPledges, + currency, + }) => { + const receivedPercentage = receivedPledges / monthlyGoal; + const totalPercentage = totalPledges / monthlyGoal; - return ( - - - - - - - - {name} - - - - {monthlyGoal && ( - - - {t('Goal')} - - - {currencyFormat(monthlyGoal, currency)} - - - )} - - - {t('Gifts Started')} - - - {isNaN(receivedPercentage) - ? '-' - : percentageFormat(receivedPercentage)} - - - - - {t('Committed')} - - - {isNaN(totalPercentage) - ? '-' - : percentageFormat(totalPercentage)} - - - - - - - - - ); - }, - )} - - - - - ); + return ( + + + + + + + + {name} + + + + {monthlyGoal && ( + + + {t('Goal')} + + + {currencyFormat(monthlyGoal, currency)} + + + )} + + + {t('Gifts Started')} + + + {isNaN(receivedPercentage) + ? '-' + : percentageFormat(receivedPercentage)} + + + + + {t('Committed')} + + + {isNaN(totalPercentage) + ? '-' + : percentageFormat(totalPercentage)} + + + + + + + + + ); + }, + )} + + + + + ); }; export default AccountLists; diff --git a/src/components/AnimatedBox/AnimatedBox.test.tsx b/src/components/AnimatedBox/AnimatedBox.test.tsx index 7912f36a4a..ea6fa68ee3 100644 --- a/src/components/AnimatedBox/AnimatedBox.test.tsx +++ b/src/components/AnimatedBox/AnimatedBox.test.tsx @@ -3,13 +3,13 @@ import { render } from '@testing-library/react'; import AnimatedBox from '.'; describe('AnimatedBox', () => { - it('has correct defaults', () => { - const { getByTestId } = render( - -
- , - ); - expect(getByTestId('TestAnimatedBox')).toBeInTheDocument(); - expect(getByTestId('TestAnimatedBoxContent')).toBeInTheDocument(); - }); + it('has correct defaults', () => { + const { getByTestId } = render( + +
+ , + ); + expect(getByTestId('TestAnimatedBox')).toBeInTheDocument(); + expect(getByTestId('TestAnimatedBoxContent')).toBeInTheDocument(); + }); }); diff --git a/src/components/AnimatedBox/AnimatedBox.tsx b/src/components/AnimatedBox/AnimatedBox.tsx index 02a05b5375..f86d979725 100644 --- a/src/components/AnimatedBox/AnimatedBox.tsx +++ b/src/components/AnimatedBox/AnimatedBox.tsx @@ -3,29 +3,29 @@ import { Box, BoxProps } from '@material-ui/core'; import { motion } from 'framer-motion'; const variants = { - initial: { - opacity: 0, + initial: { + opacity: 0, + }, + animate: { + opacity: 1, + transition: { + duration: 0.5, + ease: [0.48, 0.15, 0.25, 0.96], }, - animate: { - opacity: 1, - transition: { - duration: 0.5, - ease: [0.48, 0.15, 0.25, 0.96], - }, - }, - exit: { - opacity: 0, - transition: { - duration: 0.2, - ease: [0.48, 0.15, 0.25, 0.96], - }, + }, + exit: { + opacity: 0, + transition: { + duration: 0.2, + ease: [0.48, 0.15, 0.25, 0.96], }, + }, }; const AnimatedBox = (props: BoxProps): ReactElement => ( - - - + + + ); export default AnimatedBox; diff --git a/src/components/AnimatedCard/AnimatedCard.test.tsx b/src/components/AnimatedCard/AnimatedCard.test.tsx index 566475e14b..5a6ba945fd 100644 --- a/src/components/AnimatedCard/AnimatedCard.test.tsx +++ b/src/components/AnimatedCard/AnimatedCard.test.tsx @@ -3,13 +3,13 @@ import { render } from '@testing-library/react'; import AnimatedCard from '.'; describe('AnimatedCard', () => { - it('has correct defaults', () => { - const { getByTestId } = render( - -
- , - ); - expect(getByTestId('TestAnimatedCard')).toBeInTheDocument(); - expect(getByTestId('TestAnimatedCardContent')).toBeInTheDocument(); - }); + it('has correct defaults', () => { + const { getByTestId } = render( + +
+ , + ); + expect(getByTestId('TestAnimatedCard')).toBeInTheDocument(); + expect(getByTestId('TestAnimatedCardContent')).toBeInTheDocument(); + }); }); diff --git a/src/components/AnimatedCard/AnimatedCard.tsx b/src/components/AnimatedCard/AnimatedCard.tsx index f4a767f162..8ad25db0f8 100644 --- a/src/components/AnimatedCard/AnimatedCard.tsx +++ b/src/components/AnimatedCard/AnimatedCard.tsx @@ -3,35 +3,35 @@ import { Card, CardProps } from '@material-ui/core'; import { motion } from 'framer-motion'; const variants = { - initial: { - scale: 0.96, - y: 30, - opacity: 0, + initial: { + scale: 0.96, + y: 30, + opacity: 0, + }, + animate: { + scale: 1, + y: 0, + opacity: 1, + transition: { + duration: 0.5, + ease: [0.48, 0.15, 0.25, 0.96], }, - animate: { - scale: 1, - y: 0, - opacity: 1, - transition: { - duration: 0.5, - ease: [0.48, 0.15, 0.25, 0.96], - }, - }, - exit: { - scale: 0.6, - y: 100, - opacity: 0, - transition: { - duration: 0.2, - ease: [0.48, 0.15, 0.25, 0.96], - }, + }, + exit: { + scale: 0.6, + y: 100, + opacity: 0, + transition: { + duration: 0.2, + ease: [0.48, 0.15, 0.25, 0.96], }, + }, }; const AnimatedCard = (props: CardProps): ReactElement => ( - - - + + + ); export default AnimatedCard; diff --git a/src/components/App/Provider.tsx b/src/components/App/Provider.tsx index 6b2efb385e..f3d6ff8915 100644 --- a/src/components/App/Provider.tsx +++ b/src/components/App/Provider.tsx @@ -1,4 +1,10 @@ -import React, { ReactNode, ReactElement, useState, useReducer, Dispatch } from 'react'; +import React, { + ReactNode, + ReactElement, + useState, + useReducer, + Dispatch, +} from 'react'; import { v4 as uuidv4 } from 'uuid'; import { omit, remove, find } from 'lodash/fp'; import TaskDrawer, { TaskDrawerProps } from '../Task/Drawer/Drawer'; @@ -7,65 +13,72 @@ import rootReducer, { Action, AppState } from './rootReducer'; import { AppContext } from '.'; export interface AppProviderContext { - openTaskDrawer: (props: TaskDrawerProps) => void; - state: AppState; - dispatch: Dispatch; + openTaskDrawer: (props: TaskDrawerProps) => void; + state: AppState; + dispatch: Dispatch; } interface Props { - children: ReactNode; - initialState?: Partial; + children: ReactNode; + initialState?: Partial; } interface TaskDrawerPropsWithId extends TaskDrawerProps { - id: string; + id: string; } const AppProvider = ({ initialState, children }: Props): ReactElement => { - const [taskDrawers, setTaskDrawers] = useState([]); - const [state, dispatch] = useReducer(rootReducer, { - accountListId: null, - breadcrumb: null, - ...initialState, - }); + const [taskDrawers, setTaskDrawers] = useState([]); + const [state, dispatch] = useReducer(rootReducer, { + accountListId: null, + breadcrumb: null, + ...initialState, + }); - const openTaskDrawer = (taskDrawerProps: TaskDrawerProps): void => { - const id = uuidv4(); - if ( - !taskDrawerProps.taskId || - !find({ taskId: taskDrawerProps.taskId, showCompleteForm: taskDrawerProps.showCompleteForm }, taskDrawers) - ) { - setTaskDrawers([ - ...taskDrawers, - { - id, - ...taskDrawerProps, - onClose: (): void => { - taskDrawerProps.onClose && taskDrawerProps.onClose(); - setTimeout( - () => setTaskDrawers((taskDrawers) => remove({ id }, taskDrawers)), - theme.transitions.duration.leavingScreen, - ); - }, - }, - ]); - } - }; + const openTaskDrawer = (taskDrawerProps: TaskDrawerProps): void => { + const id = uuidv4(); + if ( + !taskDrawerProps.taskId || + !find( + { + taskId: taskDrawerProps.taskId, + showCompleteForm: taskDrawerProps.showCompleteForm, + }, + taskDrawers, + ) + ) { + setTaskDrawers([ + ...taskDrawers, + { + id, + ...taskDrawerProps, + onClose: (): void => { + taskDrawerProps.onClose && taskDrawerProps.onClose(); + setTimeout( + () => + setTaskDrawers((taskDrawers) => remove({ id }, taskDrawers)), + theme.transitions.duration.leavingScreen, + ); + }, + }, + ]); + } + }; - const value: AppProviderContext = { - openTaskDrawer, - state, - dispatch, - }; + const value: AppProviderContext = { + openTaskDrawer, + state, + dispatch, + }; - return ( - - {children} - {taskDrawers.map((props: TaskDrawerPropsWithId) => ( - - ))} - - ); + return ( + + {children} + {taskDrawers.map((props: TaskDrawerPropsWithId) => ( + + ))} + + ); }; export default AppProvider; diff --git a/src/components/App/rootReducer.test.ts b/src/components/App/rootReducer.test.ts index fc174babd8..363838f8df 100644 --- a/src/components/App/rootReducer.test.ts +++ b/src/components/App/rootReducer.test.ts @@ -1,30 +1,33 @@ import rootReducer, { Action, AppState } from './rootReducer'; describe('rootReducer', () => { - const state: AppState = { - accountListId: null, - breadcrumb: null, - }; + const state: AppState = { + accountListId: null, + breadcrumb: null, + }; - describe('updateAccountListId', () => { - it('updates accountListId state', () => { - const action: Action = { type: 'updateAccountListId', accountListId: 'abc' }; - expect(rootReducer(state, action).accountListId).toEqual('abc'); - }); + describe('updateAccountListId', () => { + it('updates accountListId state', () => { + const action: Action = { + type: 'updateAccountListId', + accountListId: 'abc', + }; + expect(rootReducer(state, action).accountListId).toEqual('abc'); }); + }); - describe('updateBreadcrumb', () => { - it('updates breadcrumb state', () => { - const action: Action = { type: 'updateBreadcrumb', breadcrumb: 'abc' }; - expect(rootReducer(state, action).breadcrumb).toEqual('abc'); - }); + describe('updateBreadcrumb', () => { + it('updates breadcrumb state', () => { + const action: Action = { type: 'updateBreadcrumb', breadcrumb: 'abc' }; + expect(rootReducer(state, action).breadcrumb).toEqual('abc'); }); + }); - describe('updateUser', () => { - it('updates user state', () => { - const user = { id: 'user-1', firstName: 'John', lastName: 'Smith' }; - const action: Action = { type: 'updateUser', user }; - expect(rootReducer(state, action).user).toEqual(user); - }); + describe('updateUser', () => { + it('updates user state', () => { + const user = { id: 'user-1', firstName: 'John', lastName: 'Smith' }; + const action: Action = { type: 'updateUser', user }; + expect(rootReducer(state, action).user).toEqual(user); }); + }); }); diff --git a/src/components/App/rootReducer.ts b/src/components/App/rootReducer.ts index 5e1f452733..3b5c4fc489 100644 --- a/src/components/App/rootReducer.ts +++ b/src/components/App/rootReducer.ts @@ -1,41 +1,44 @@ export interface AppState { - accountListId: string; - breadcrumb: string; - user?: User; + accountListId: string; + breadcrumb: string; + user?: User; } interface User { - id: string; - firstName: string; - lastName: string; + id: string; + firstName: string; + lastName: string; } -export type Action = UpdateAccountListIdAction | UpdateBreadcrumbAction | UpdateUserAction; +export type Action = + | UpdateAccountListIdAction + | UpdateBreadcrumbAction + | UpdateUserAction; type UpdateAccountListIdAction = { - type: 'updateAccountListId'; - accountListId: string; + type: 'updateAccountListId'; + accountListId: string; }; type UpdateBreadcrumbAction = { - type: 'updateBreadcrumb'; - breadcrumb: string; + type: 'updateBreadcrumb'; + breadcrumb: string; }; type UpdateUserAction = { - type: 'updateUser'; - user: User; + type: 'updateUser'; + user: User; }; const rootReducer = (state: AppState, action: Action): AppState => { - switch (action.type) { - case 'updateAccountListId': - return { ...state, accountListId: action.accountListId }; - case 'updateBreadcrumb': - return { ...state, breadcrumb: action.breadcrumb }; - case 'updateUser': - return { ...state, user: action.user }; - } + switch (action.type) { + case 'updateAccountListId': + return { ...state, accountListId: action.accountListId }; + case 'updateBreadcrumb': + return { ...state, breadcrumb: action.breadcrumb }; + case 'updateUser': + return { ...state, user: action.user }; + } }; export default rootReducer; diff --git a/src/components/Dashboard/Balance/Balance.stories.tsx b/src/components/Dashboard/Balance/Balance.stories.tsx index d22c79e678..6cbfceea0e 100644 --- a/src/components/Dashboard/Balance/Balance.stories.tsx +++ b/src/components/Dashboard/Balance/Balance.stories.tsx @@ -3,27 +3,27 @@ import { Box } from '@material-ui/core'; import Balance from '.'; export default { - title: 'Dashboard/Balance', + title: 'Dashboard/Balance', }; export const Default = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Empty = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; diff --git a/src/components/Dashboard/Balance/Balance.test.tsx b/src/components/Dashboard/Balance/Balance.test.tsx index 3300c982f0..4a6a88031e 100644 --- a/src/components/Dashboard/Balance/Balance.test.tsx +++ b/src/components/Dashboard/Balance/Balance.test.tsx @@ -3,22 +3,26 @@ import { render } from '@testing-library/react'; import Balance from '.'; describe('Balance', () => { - it('default', () => { - const { getByTestId, getByRole } = render(); - expect(getByTestId('BalanceTypography').textContent).toEqual('$1,001'); - expect(getByRole('link', { name: 'View Gifts' })).toHaveAttribute( - 'href', - 'https://stage.mpdx.org/reports/donations', - ); - }); + it('default', () => { + const { getByTestId, getByRole } = render(); + expect(getByTestId('BalanceTypography').textContent).toEqual('$1,001'); + expect(getByRole('link', { name: 'View Gifts' })).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/reports/donations', + ); + }); - it('custom props', () => { - const { getByTestId } = render(); - expect(getByTestId('BalanceTypography').textContent).toEqual('€1,001'); - }); + it('custom props', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('BalanceTypography').textContent).toEqual('€1,001'); + }); - it('loading', () => { - const { getByTestId } = render(); - expect(getByTestId('BalanceTypography').children[0].className).toContain('MuiSkeleton-root'); - }); + it('loading', () => { + const { getByTestId } = render(); + expect(getByTestId('BalanceTypography').children[0].className).toContain( + 'MuiSkeleton-root', + ); + }); }); diff --git a/src/components/Dashboard/Balance/Balance.tsx b/src/components/Dashboard/Balance/Balance.tsx index 4f7ffcfa34..30316f4975 100644 --- a/src/components/Dashboard/Balance/Balance.tsx +++ b/src/components/Dashboard/Balance/Balance.tsx @@ -1,5 +1,13 @@ import React, { ReactElement } from 'react'; -import { makeStyles, Theme, CardContent, Typography, CardActions, Button, Box } from '@material-ui/core'; +import { + makeStyles, + Theme, + CardContent, + Typography, + CardActions, + Button, + Box, +} from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; import { useTranslation } from 'react-i18next'; import { currencyFormat } from '../../../lib/intlFormat'; @@ -8,52 +16,60 @@ import AnimatedBox from '../../AnimatedBox'; import HandoffLink from '../../HandoffLink'; const useStyles = makeStyles((theme: Theme) => ({ - card: { - display: 'flex', - flexDirection: 'column', - [theme.breakpoints.up('sm')]: { - height: 'calc(100% - 65px)', - }, - }, - cardContent: { - flex: '1', + card: { + display: 'flex', + flexDirection: 'column', + [theme.breakpoints.up('sm')]: { + height: 'calc(100% - 65px)', }, + }, + cardContent: { + flex: '1', + }, })); interface Props { - loading?: boolean; - balance?: number; - currencyCode?: string; + loading?: boolean; + balance?: number; + currencyCode?: string; } -const Balance = ({ loading, balance, currencyCode = 'USD' }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); +const Balance = ({ + loading, + balance, + currencyCode = 'USD', +}: Props): ReactElement => { + const classes = useStyles(); + const { t } = useTranslation(); - return ( - <> - - - {t('Account Balance')} - - - - - - {loading ? : currencyFormat(balance, currencyCode)} - - {t('It may take a few days to update.')} - - - - - - - - - ); + return ( + <> + + + {t('Account Balance')} + + + + + + {loading ? ( + + ) : ( + currencyFormat(balance, currencyCode) + )} + + {t('It may take a few days to update.')} + + + + + + + + + ); }; export default Balance; diff --git a/src/components/Dashboard/Dashboard.stories.tsx b/src/components/Dashboard/Dashboard.stories.tsx index 58dccd37ab..d184ecf4f5 100644 --- a/src/components/Dashboard/Dashboard.stories.tsx +++ b/src/components/Dashboard/Dashboard.stories.tsx @@ -5,105 +5,105 @@ import { GetThisWeekDefaultMocks } from './ThisWeek/ThisWeek.mock'; import Dashboard from '.'; export default { - title: 'Dashboard', + title: 'Dashboard', }; export const Default = (): ReactElement => { - const data: GetDashboardQuery = { - user: { - firstName: 'Roger', + const data: GetDashboardQuery = { + user: { + firstName: 'Roger', + }, + accountList: { + name: 'My Account List', + monthlyGoal: 1000, + receivedPledges: 400, + totalPledges: 700, + currency: 'USD', + balance: 1000, + }, + reportsDonationHistories: { + periods: [ + { + convertedTotal: 200, + startDate: '2011-12-1', + totals: [ + { + currency: 'USD', + convertedAmount: 350, + }, + ], + }, + { + convertedTotal: 400, + startDate: '2012-1-1', + totals: [ + { + currency: 'USD', + convertedAmount: 750, + }, + ], }, - accountList: { - name: 'My Account List', - monthlyGoal: 1000, - receivedPledges: 400, - totalPledges: 700, - currency: 'USD', - balance: 1000, + { + convertedTotal: 900, + startDate: '2012-2-1', + totals: [ + { + currency: 'USD', + convertedAmount: 550, + }, + { + currency: 'NZD', + convertedAmount: 400, + }, + { + currency: 'CAD', + convertedAmount: 200, + }, + { + currency: 'AUD', + convertedAmount: 100, + }, + ], }, - reportsDonationHistories: { - periods: [ - { - convertedTotal: 200, - startDate: '2011-12-1', - totals: [ - { - currency: 'USD', - convertedAmount: 350, - }, - ], - }, - { - convertedTotal: 400, - startDate: '2012-1-1', - totals: [ - { - currency: 'USD', - convertedAmount: 750, - }, - ], - }, - { - convertedTotal: 900, - startDate: '2012-2-1', - totals: [ - { - currency: 'USD', - convertedAmount: 550, - }, - { - currency: 'NZD', - convertedAmount: 400, - }, - { - currency: 'CAD', - convertedAmount: 200, - }, - { - currency: 'AUD', - convertedAmount: 100, - }, - ], - }, - { - convertedTotal: 1100, - startDate: '2012-3-1', - totals: [ - { - currency: 'USD', - convertedAmount: 950, - }, - { - currency: 'NZD', - convertedAmount: 800, - }, - { - currency: 'CAD', - convertedAmount: 300, - }, - { - currency: 'AUD', - convertedAmount: 200, - }, - { - currency: 'HKD', - convertedAmount: 100, - }, - ], - }, - ], - averageIgnoreCurrent: 750, + { + convertedTotal: 1100, + startDate: '2012-3-1', + totals: [ + { + currency: 'USD', + convertedAmount: 950, + }, + { + currency: 'NZD', + convertedAmount: 800, + }, + { + currency: 'CAD', + convertedAmount: 300, + }, + { + currency: 'AUD', + convertedAmount: 200, + }, + { + currency: 'HKD', + convertedAmount: 100, + }, + ], }, - }; - return ( - - - - ); + ], + averageIgnoreCurrent: 750, + }, + }; + return ( + + + + ); }; Default.story = { - parameters: { - chromatic: { delay: 1000 }, - }, + parameters: { + chromatic: { delay: 1000 }, + }, }; diff --git a/src/components/Dashboard/Dashboard.test.tsx b/src/components/Dashboard/Dashboard.test.tsx index ecdd4889df..e802167e8c 100644 --- a/src/components/Dashboard/Dashboard.test.tsx +++ b/src/components/Dashboard/Dashboard.test.tsx @@ -8,140 +8,177 @@ import { GetThisWeekDefaultMocks } from './ThisWeek/ThisWeek.mock'; import Dashboard from '.'; jest.mock('../App', () => ({ - useApp: (): Partial => ({ - openTaskDrawer: jest.fn(), - }), + useApp: (): Partial => ({ + openTaskDrawer: jest.fn(), + }), })); const data: GetDashboardQuery = { - user: { - firstName: 'Roger', - }, - accountList: { - name: 'My Account List', - monthlyGoal: 1000, - receivedPledges: 400, - totalPledges: 700, - currency: 'NZD', - balance: 1000, - }, - reportsDonationHistories: { - periods: [ - { - convertedTotal: 200, - startDate: '2011-12-1', - totals: [ - { - currency: 'USD', - convertedAmount: 350, - }, - ], - }, - { - convertedTotal: 400, - startDate: '2012-1-1', - totals: [ - { - currency: 'USD', - convertedAmount: 750, - }, - ], - }, - { - convertedTotal: 900, - startDate: '2012-2-1', - totals: [ - { - currency: 'USD', - convertedAmount: 550, - }, - { - currency: 'NZD', - convertedAmount: 400, - }, - { - currency: 'CAD', - convertedAmount: 200, - }, - { - currency: 'AUD', - convertedAmount: 100, - }, - ], - }, - { - convertedTotal: 1100, - startDate: '2012-3-1', - totals: [ - { - currency: 'USD', - convertedAmount: 950, - }, - { - currency: 'NZD', - convertedAmount: 800, - }, - { - currency: 'CAD', - convertedAmount: 300, - }, - { - currency: 'AUD', - convertedAmount: 200, - }, - { - currency: 'HKD', - convertedAmount: 100, - }, - ], - }, + user: { + firstName: 'Roger', + }, + accountList: { + name: 'My Account List', + monthlyGoal: 1000, + receivedPledges: 400, + totalPledges: 700, + currency: 'NZD', + balance: 1000, + }, + reportsDonationHistories: { + periods: [ + { + convertedTotal: 200, + startDate: '2011-12-1', + totals: [ + { + currency: 'USD', + convertedAmount: 350, + }, ], - averageIgnoreCurrent: 750, - }, + }, + { + convertedTotal: 400, + startDate: '2012-1-1', + totals: [ + { + currency: 'USD', + convertedAmount: 750, + }, + ], + }, + { + convertedTotal: 900, + startDate: '2012-2-1', + totals: [ + { + currency: 'USD', + convertedAmount: 550, + }, + { + currency: 'NZD', + convertedAmount: 400, + }, + { + currency: 'CAD', + convertedAmount: 200, + }, + { + currency: 'AUD', + convertedAmount: 100, + }, + ], + }, + { + convertedTotal: 1100, + startDate: '2012-3-1', + totals: [ + { + currency: 'USD', + convertedAmount: 950, + }, + { + currency: 'NZD', + convertedAmount: 800, + }, + { + currency: 'CAD', + convertedAmount: 300, + }, + { + currency: 'AUD', + convertedAmount: 200, + }, + { + currency: 'HKD', + convertedAmount: 100, + }, + ], + }, + ], + averageIgnoreCurrent: 750, + }, }; describe('Dashboard', () => { - beforeEach(() => { - matchMediaMock({ width: '1024px' }); - }); + beforeEach(() => { + matchMediaMock({ width: '1024px' }); + }); - it('default', async () => { - const { getByTestId, queryByTestId } = render( - - - , - ); - await waitFor(() => expect(queryByTestId('PartnerCarePrayerListLoading')).not.toBeInTheDocument()); - expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual('NZ$1,000'); - expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual('NZ$700'); - expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual('NZ$400'); - expect(getByTestId('BalanceTypography').textContent).toEqual('NZ$1,000'); - expect(getByTestId('DonationHistoriesTypographyGoal').textContent).toEqual('Goal NZ$1,000'); - expect(getByTestId('DonationHistoriesTypographyAverage').textContent).toEqual('Average NZ$750'); - expect(getByTestId('DonationHistoriesTypographyPledged').textContent).toEqual('Committed NZ$700'); - expect(getByTestId('PartnerCarePrayerList')).toBeInTheDocument(); - expect(getByTestId('TasksDueThisWeekList')).toBeInTheDocument(); - expect(getByTestId('LateCommitmentsListContacts')).toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecentList')).toBeInTheDocument(); - expect(getByTestId('AppealsBoxName')).toBeInTheDocument(); - }); + it('default', async () => { + const { getByTestId, queryByTestId } = render( + + + , + ); + await waitFor(() => + expect( + queryByTestId('PartnerCarePrayerListLoading'), + ).not.toBeInTheDocument(), + ); + expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual( + 'NZ$1,000', + ); + expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual( + 'NZ$700', + ); + expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual( + 'NZ$400', + ); + expect(getByTestId('BalanceTypography').textContent).toEqual('NZ$1,000'); + expect(getByTestId('DonationHistoriesTypographyGoal').textContent).toEqual( + 'Goal NZ$1,000', + ); + expect( + getByTestId('DonationHistoriesTypographyAverage').textContent, + ).toEqual('Average NZ$750'); + expect( + getByTestId('DonationHistoriesTypographyPledged').textContent, + ).toEqual('Committed NZ$700'); + expect(getByTestId('PartnerCarePrayerList')).toBeInTheDocument(); + expect(getByTestId('TasksDueThisWeekList')).toBeInTheDocument(); + expect(getByTestId('LateCommitmentsListContacts')).toBeInTheDocument(); + expect(getByTestId('ReferralsTabRecentList')).toBeInTheDocument(); + expect(getByTestId('AppealsBoxName')).toBeInTheDocument(); + }); - it('handles null fields', async () => { - const { getByTestId, queryByTestId } = render( - - - , - ); - await waitFor(() => expect(queryByTestId('PartnerCarePrayerListLoading')).not.toBeInTheDocument()); - expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual('$0'); - expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual('$700'); - expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual('$400'); - expect(getByTestId('BalanceTypography').textContent).toEqual('$1,000'); - expect(queryByTestId('DonationHistoriesTypographyGoal')).not.toBeInTheDocument(); - expect(getByTestId('DonationHistoriesTypographyAverage').textContent).toEqual('Average $750'); - expect(getByTestId('DonationHistoriesTypographyPledged').textContent).toEqual('Committed $700'); - }); + it('handles null fields', async () => { + const { getByTestId, queryByTestId } = render( + + + , + ); + await waitFor(() => + expect( + queryByTestId('PartnerCarePrayerListLoading'), + ).not.toBeInTheDocument(), + ); + expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual('$0'); + expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual( + '$700', + ); + expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual( + '$400', + ); + expect(getByTestId('BalanceTypography').textContent).toEqual('$1,000'); + expect( + queryByTestId('DonationHistoriesTypographyGoal'), + ).not.toBeInTheDocument(); + expect( + getByTestId('DonationHistoriesTypographyAverage').textContent, + ).toEqual('Average $750'); + expect( + getByTestId('DonationHistoriesTypographyPledged').textContent, + ).toEqual('Committed $700'); + }); }); diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index acd8e45695..4aaa1dd67c 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -9,60 +9,68 @@ import DonationHistories from './DonationHistories'; import ThisWeek from './ThisWeek'; interface Props { - data: GetDashboardQuery; - accountListId: string; + data: GetDashboardQuery; + accountListId: string; } const variants = { - animate: { - transition: { - delayChildren: 1, - staggerChildren: 0.15, - }, + animate: { + transition: { + delayChildren: 1, + staggerChildren: 0.15, }, - exit: { - transition: { - staggerChildren: 0.1, - }, + }, + exit: { + transition: { + staggerChildren: 0.1, }, + }, }; const Dashboard = ({ data, accountListId }: Props): ReactElement => { - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - ); + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + ); }; export default Dashboard; diff --git a/src/components/Dashboard/DonationHistories/DonationHistories.stories.tsx b/src/components/Dashboard/DonationHistories/DonationHistories.stories.tsx index 050697ea0f..0750474234 100644 --- a/src/components/Dashboard/DonationHistories/DonationHistories.stories.tsx +++ b/src/components/Dashboard/DonationHistories/DonationHistories.stories.tsx @@ -3,142 +3,147 @@ import { Box } from '@material-ui/core'; import DonationHistories from '.'; export default { - title: 'Dashboard/DonationHistories', + title: 'Dashboard/DonationHistories', }; const reportsDonationHistories = { - periods: [ + periods: [ + { + convertedTotal: 200, + startDate: '2011-12-1', + totals: [ { - convertedTotal: 200, - startDate: '2011-12-1', - totals: [ - { - currency: 'USD', - convertedAmount: 350, - }, - ], + currency: 'USD', + convertedAmount: 350, }, + ], + }, + { + convertedTotal: 400, + startDate: '2012-1-1', + totals: [ { - convertedTotal: 400, - startDate: '2012-1-1', - totals: [ - { - currency: 'USD', - convertedAmount: 750, - }, - ], + currency: 'USD', + convertedAmount: 750, }, + ], + }, + { + convertedTotal: 900, + startDate: '2012-2-1', + totals: [ { - convertedTotal: 900, - startDate: '2012-2-1', - totals: [ - { - currency: 'USD', - convertedAmount: 550, - }, - { - currency: 'NZD', - convertedAmount: 400, - }, - { - currency: 'CAD', - convertedAmount: 200, - }, - { - currency: 'AUD', - convertedAmount: 100, - }, - ], + currency: 'USD', + convertedAmount: 550, }, { - convertedTotal: 1100, - startDate: '2012-3-1', - totals: [ - { - currency: 'USD', - convertedAmount: 950, - }, - { - currency: 'NZD', - convertedAmount: 800, - }, - { - currency: 'CAD', - convertedAmount: 300, - }, - { - currency: 'AUD', - convertedAmount: 200, - }, - { - currency: 'HKD', - convertedAmount: 100, - }, - ], + currency: 'NZD', + convertedAmount: 400, }, - ], - averageIgnoreCurrent: 750, + { + currency: 'CAD', + convertedAmount: 200, + }, + { + currency: 'AUD', + convertedAmount: 100, + }, + ], + }, + { + convertedTotal: 1100, + startDate: '2012-3-1', + totals: [ + { + currency: 'USD', + convertedAmount: 950, + }, + { + currency: 'NZD', + convertedAmount: 800, + }, + { + currency: 'CAD', + convertedAmount: 300, + }, + { + currency: 'AUD', + convertedAmount: 200, + }, + { + currency: 'HKD', + convertedAmount: 100, + }, + ], + }, + ], + averageIgnoreCurrent: 750, }; export const Default = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const WithReferences = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Empty = (): ReactElement => { - const emptyReportsDonationHistories = { - periods: [ - { - convertedTotal: 0, - startDate: '2011-12-1', - totals: [], - }, - { - convertedTotal: 0, - startDate: '2012-1-1', - totals: [], - }, - { - convertedTotal: 0, - startDate: '2012-2-1', - totals: [], - }, - { - convertedTotal: 0, - startDate: '2012-3-1', - totals: [], - }, - ], - averageIgnoreCurrent: 0, - }; + const emptyReportsDonationHistories = { + periods: [ + { + convertedTotal: 0, + startDate: '2011-12-1', + totals: [], + }, + { + convertedTotal: 0, + startDate: '2012-1-1', + totals: [], + }, + { + convertedTotal: 0, + startDate: '2012-2-1', + totals: [], + }, + { + convertedTotal: 0, + startDate: '2012-3-1', + totals: [], + }, + ], + averageIgnoreCurrent: 0, + }; - return ( - - - - ); + return ( + + + + ); }; diff --git a/src/components/Dashboard/DonationHistories/DonationHistories.test.tsx b/src/components/Dashboard/DonationHistories/DonationHistories.test.tsx index 59103ec049..a2108b26e0 100644 --- a/src/components/Dashboard/DonationHistories/DonationHistories.test.tsx +++ b/src/components/Dashboard/DonationHistories/DonationHistories.test.tsx @@ -3,69 +3,85 @@ import { render } from '@testing-library/react'; import DonationHistories from '.'; describe('DonationHistories', () => { - let reportsDonationHistories; + let reportsDonationHistories; - it('default', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('DonationHistoriesBoxEmpty')).toBeInTheDocument(); - expect(queryByTestId('DonationHistoriesGridLoading')).not.toBeInTheDocument(); - }); + it('default', () => { + const { getByTestId, queryByTestId } = render(); + expect(getByTestId('DonationHistoriesBoxEmpty')).toBeInTheDocument(); + expect( + queryByTestId('DonationHistoriesGridLoading'), + ).not.toBeInTheDocument(); + }); - it('empty periods', () => { - reportsDonationHistories = { - periods: [ - { - convertedTotal: 0, - startDate: '1-1-2019', - totals: [{ currency: 'USD', convertedAmount: 0 }], - }, - { - convertedTotal: 0, - startDate: '1-2-2019', - totals: [{ currency: 'NZD', convertedAmount: 0 }], - }, - ], - averageIgnoreCurrent: 0, - }; - const { getByTestId, queryByTestId } = render( - , - ); - expect(getByTestId('DonationHistoriesBoxEmpty')).toBeInTheDocument(); - expect(queryByTestId('DonationHistoriesGridLoading')).not.toBeInTheDocument(); - }); + it('empty periods', () => { + reportsDonationHistories = { + periods: [ + { + convertedTotal: 0, + startDate: '1-1-2019', + totals: [{ currency: 'USD', convertedAmount: 0 }], + }, + { + convertedTotal: 0, + startDate: '1-2-2019', + totals: [{ currency: 'NZD', convertedAmount: 0 }], + }, + ], + averageIgnoreCurrent: 0, + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('DonationHistoriesBoxEmpty')).toBeInTheDocument(); + expect( + queryByTestId('DonationHistoriesGridLoading'), + ).not.toBeInTheDocument(); + }); - it('loading', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('DonationHistoriesGridLoading')).toBeInTheDocument(); - expect(queryByTestId('DonationHistoriesBoxEmpty')).not.toBeInTheDocument(); - }); + it('loading', () => { + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('DonationHistoriesGridLoading')).toBeInTheDocument(); + expect(queryByTestId('DonationHistoriesBoxEmpty')).not.toBeInTheDocument(); + }); - describe('populated periods', () => { - beforeEach(() => { - reportsDonationHistories = { - periods: [ - { - convertedTotal: 50, - startDate: '1-1-2019', - totals: [{ currency: 'USD', convertedAmount: 50 }], - }, - { - convertedTotal: 60, - startDate: '1-2-2019', - totals: [{ currency: 'NZD', convertedAmount: 60 }], - }, - ], - averageIgnoreCurrent: 1000, - }; - }); + describe('populated periods', () => { + beforeEach(() => { + reportsDonationHistories = { + periods: [ + { + convertedTotal: 50, + startDate: '1-1-2019', + totals: [{ currency: 'USD', convertedAmount: 50 }], + }, + { + convertedTotal: 60, + startDate: '1-2-2019', + totals: [{ currency: 'NZD', convertedAmount: 60 }], + }, + ], + averageIgnoreCurrent: 1000, + }; + }); - it('shows references', () => { - const { getByTestId } = render( - , - ); - expect(getByTestId('DonationHistoriesTypographyGoal').textContent).toEqual('Goal $100'); - expect(getByTestId('DonationHistoriesTypographyAverage').textContent).toEqual('Average $1,000'); - expect(getByTestId('DonationHistoriesTypographyPledged').textContent).toEqual('Committed $2,500'); - }); + it('shows references', () => { + const { getByTestId } = render( + , + ); + expect( + getByTestId('DonationHistoriesTypographyGoal').textContent, + ).toEqual('Goal $100'); + expect( + getByTestId('DonationHistoriesTypographyAverage').textContent, + ).toEqual('Average $1,000'); + expect( + getByTestId('DonationHistoriesTypographyPledged').textContent, + ).toEqual('Committed $2,500'); }); + }); }); diff --git a/src/components/Dashboard/DonationHistories/DonationHistories.tsx b/src/components/Dashboard/DonationHistories/DonationHistories.tsx index ecdb1decae..46eaac08ee 100644 --- a/src/components/Dashboard/DonationHistories/DonationHistories.tsx +++ b/src/components/Dashboard/DonationHistories/DonationHistories.tsx @@ -1,16 +1,24 @@ import React, { ReactElement } from 'react'; -import { CardContent, Box, Typography, Grid, CardHeader, makeStyles, Theme } from '@material-ui/core'; import { - ReferenceLine, - BarChart, - Bar, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - Legend, - ResponsiveContainer, - Text, + CardContent, + Box, + Typography, + Grid, + CardHeader, + makeStyles, + Theme, +} from '@material-ui/core'; +import { + ReferenceLine, + BarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + Text, } from 'recharts'; import moment from 'moment'; import { Skeleton } from '@material-ui/lab'; @@ -21,262 +29,313 @@ import AnimatedBox from '../../AnimatedBox'; import illustration15 from '../../../images/drawkit/grape/drawkit-grape-pack-illustration-15.svg'; const useStyles = makeStyles((theme: Theme) => ({ - cardHeader: { - textAlign: 'center', - }, - lineKey: { - display: 'inline-block', - height: '5px', - width: '20px', - marginRight: '10px', - marginBottom: '4px', - borderRadius: '5px', - }, - lineKeyGoal: { - backgroundColor: '#17AEBF', - }, - lineKeyAverage: { - backgroundColor: '#9C9FA1', - }, - lineKeyPledged: { - backgroundColor: '#FFCF07', - }, - boxImg: { - display: 'flex', - flex: 1, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - padding: theme.spacing(2), - [theme.breakpoints.down('xs')]: { - padding: theme.spacing(0), - }, + cardHeader: { + textAlign: 'center', + }, + lineKey: { + display: 'inline-block', + height: '5px', + width: '20px', + marginRight: '10px', + marginBottom: '4px', + borderRadius: '5px', + }, + lineKeyGoal: { + backgroundColor: '#17AEBF', + }, + lineKeyAverage: { + backgroundColor: '#9C9FA1', + }, + lineKeyPledged: { + backgroundColor: '#FFCF07', + }, + boxImg: { + display: 'flex', + flex: 1, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + padding: theme.spacing(2), + [theme.breakpoints.down('xs')]: { + padding: theme.spacing(0), }, - img: { - height: '248px', - marginBottom: theme.spacing(2), - [theme.breakpoints.down('xs')]: { - height: '114px', - }, + }, + img: { + height: '248px', + marginBottom: theme.spacing(2), + [theme.breakpoints.down('xs')]: { + height: '114px', }, + }, })); interface Props { - loading?: boolean; - reportsDonationHistories?: { - periods: { - convertedTotal: number; - startDate: string; - totals: { currency: string; convertedAmount: number }[]; - }[]; - averageIgnoreCurrent: number; - }; - currencyCode?: string; - goal?: number; - pledged?: number; + loading?: boolean; + reportsDonationHistories?: { + periods: { + convertedTotal: number; + startDate: string; + totals: { currency: string; convertedAmount: number }[]; + }[]; + averageIgnoreCurrent: number; + }; + currencyCode?: string; + goal?: number; + pledged?: number; } const DonationHistories = ({ - loading, - reportsDonationHistories, - goal, - pledged, - currencyCode = 'USD', + loading, + reportsDonationHistories, + goal, + pledged, + currencyCode = 'USD', }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); - const fills = ['#FFCF07', '#30F2F2', '#1FC0D2', '#007398']; - const currencies: { dataKey: string; fill: string }[] = []; - const periods = reportsDonationHistories?.periods?.map((period) => { - const data = { startDate: moment(period.startDate).format('MMM YY'), total: period.convertedTotal }; - period.totals.forEach((total) => { - if (!currencies.find((currency) => total.currency == currency.dataKey)) { - currencies.push({ dataKey: total.currency, fill: fills.pop() }); - } - data[total.currency] = total.convertedAmount; - }); - return data; + const classes = useStyles(); + const { t } = useTranslation(); + const fills = ['#FFCF07', '#30F2F2', '#1FC0D2', '#007398']; + const currencies: { dataKey: string; fill: string }[] = []; + const periods = reportsDonationHistories?.periods?.map((period) => { + const data = { + startDate: moment(period.startDate).format('MMM YY'), + total: period.convertedTotal, + }; + period.totals.forEach((total) => { + if (!currencies.find((currency) => total.currency == currency.dataKey)) { + currencies.push({ dataKey: total.currency, fill: fills.pop() }); + } + data[total.currency] = total.convertedAmount; }); - const empty = !loading && (periods === undefined || periods.reduce((result, { total }) => result + total, 0) === 0); + return data; + }); + const empty = + !loading && + (periods === undefined || + periods.reduce((result, { total }) => result + total, 0) === 0); - return ( - <> - - - Monthly Activity - + return ( + <> + + + Monthly Activity + + + + {!empty && ( + + + + {goal ? ( + <> + + + + {t('Goal')}{' '} + {currencyFormat(goal, currencyCode)} + + + | + + ) : null} + + + + {t('Average')}{' '} + {loading ? ( + + ) : ( + currencyFormat( + reportsDonationHistories.averageIgnoreCurrent, + currencyCode, + ) + )} + + + {pledged ? ( + <> + | + + + + {t('Committed')}{' '} + {currencyFormat(pledged, currencyCode)} + + + + ) : null} + + + } + /> + + )} + + {empty ? ( + + empty + {t('No monthly activity to show.')} - - {!empty && ( - - - - {goal ? ( - <> - - - - {t('Goal')}{' '} - {currencyFormat(goal, currencyCode)} - - - | - - ) : null} - - - - {t('Average')}{' '} - {loading ? ( - - ) : ( - currencyFormat( - reportsDonationHistories.averageIgnoreCurrent, - currencyCode, - ) - )} - - - {pledged ? ( - <> - | - - - - {t('Committed')}{' '} - {currencyFormat(pledged, currencyCode)} - - - - ) : null} - - + ) : ( + <> + + {loading ? ( + + + + + + + + + + + + + + + ) : ( + + + + + {goal && ( + + )} + + {pledged && ( + + )} + + + { + t('Amount ({{ currencyCode }})', { + currencyCode, + }) as string } + + } + /> + + {currencies.map((currency) => ( + - + ))} + + + )} + + + {loading ? ( + + + + + + + + + + + + + + + ) : ( + + + + + + + )} - - {empty ? ( - - empty - {t('No monthly activity to show.')} - - ) : ( - <> - - {loading ? ( - - - - - - - - - - - - - - - ) : ( - - - - - {goal && } - - {pledged && } - - - {t('Amount ({{ currencyCode }})', { currencyCode }) as string} - - } - /> - - {currencies.map((currency) => ( - - ))} - - - )} - - - {loading ? ( - - - - - - - - - - - - - - - ) : ( - - - - - - - - )} - - - )} - - - - ); + + + )} + + + + ); }; export default DonationHistories; diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.stories.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.stories.tsx index 738d198899..d09e004aa4 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.stories.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.stories.tsx @@ -3,42 +3,52 @@ import { Box } from '@material-ui/core'; import MonthlyGoal from '.'; export default { - title: 'Dashboard/MonthlyGoal', + title: 'Dashboard/MonthlyGoal', }; export const Default = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const WhenMin = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const WhenMax = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Empty = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx index 7a55fd0c8f..15d082b402 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx @@ -4,86 +4,170 @@ import matchMediaMock from '../../../../__tests__/util/matchMediaMock'; import MonthlyGoal from '.'; describe('MonthlyGoal', () => { - beforeEach(() => { - matchMediaMock({ width: '1024px' }); - }); + beforeEach(() => { + matchMediaMock({ width: '1024px' }); + }); - it('default', () => { - const { getByTestId, queryByTestId } = render(); - expect(queryByTestId('MonthlyGoalTypographyGoalMobile')).not.toBeInTheDocument(); - expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual('$0'); - expect(getByTestId('MonthlyGoalTypographyReceivedPercentage').textContent).toEqual('-'); - expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual('$0'); - expect(getByTestId('MonthlyGoalTypographyPledgedPercentage').textContent).toEqual('-'); - expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual('$0'); - expect(queryByTestId('MonthlyGoalTypographyBelowGoalPercentage')).not.toBeInTheDocument(); - expect(queryByTestId('MonthlyGoalTypographyBelowGoal')).not.toBeInTheDocument(); - expect(getByTestId('MonthlyGoalTypographyAboveGoalPercentage').textContent).toEqual('-'); - expect(getByTestId('MonthlyGoalTypographyAboveGoal').textContent).toEqual('$0'); - }); + it('default', () => { + const { getByTestId, queryByTestId } = render(); + expect( + queryByTestId('MonthlyGoalTypographyGoalMobile'), + ).not.toBeInTheDocument(); + expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual('$0'); + expect( + getByTestId('MonthlyGoalTypographyReceivedPercentage').textContent, + ).toEqual('-'); + expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual( + '$0', + ); + expect( + getByTestId('MonthlyGoalTypographyPledgedPercentage').textContent, + ).toEqual('-'); + expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual( + '$0', + ); + expect( + queryByTestId('MonthlyGoalTypographyBelowGoalPercentage'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('MonthlyGoalTypographyBelowGoal'), + ).not.toBeInTheDocument(); + expect( + getByTestId('MonthlyGoalTypographyAboveGoalPercentage').textContent, + ).toEqual('-'); + expect(getByTestId('MonthlyGoalTypographyAboveGoal').textContent).toEqual( + '$0', + ); + }); - it('loading', () => { - const { getByTestId } = render(); - expect(getByTestId('MonthlyGoalTypographyGoal').children[0].className).toContain('MuiSkeleton-root'); - expect(getByTestId('MonthlyGoalTypographyReceivedPercentage').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('MonthlyGoalTypographyReceived').children[0].className).toContain('MuiSkeleton-root'); - expect(getByTestId('MonthlyGoalTypographyPledgedPercentage').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('MonthlyGoalTypographyPledged').children[0].className).toContain('MuiSkeleton-root'); - expect(getByTestId('MonthlyGoalTypographyAboveGoalPercentage').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('MonthlyGoalTypographyAboveGoal').children[0].className).toContain('MuiSkeleton-root'); - }); + it('loading', () => { + const { getByTestId } = render(); + expect( + getByTestId('MonthlyGoalTypographyGoal').children[0].className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('MonthlyGoalTypographyReceivedPercentage').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('MonthlyGoalTypographyReceived').children[0].className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('MonthlyGoalTypographyPledgedPercentage').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('MonthlyGoalTypographyPledged').children[0].className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('MonthlyGoalTypographyAboveGoalPercentage').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('MonthlyGoalTypographyAboveGoal').children[0].className, + ).toContain('MuiSkeleton-root'); + }); - it('props', () => { - const { getByTestId, queryByTestId } = render( - , - ); - expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual('€1,000'); - expect(getByTestId('MonthlyGoalTypographyReceivedPercentage').textContent).toEqual('50%'); - expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual('€500'); - expect(getByTestId('MonthlyGoalTypographyPledgedPercentage').textContent).toEqual('75%'); - expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual('€750'); - expect(getByTestId('MonthlyGoalTypographyBelowGoalPercentage').textContent).toEqual('25%'); - expect(getByTestId('MonthlyGoalTypographyBelowGoal').textContent).toEqual('€250'); - expect(queryByTestId('MonthlyGoalTypographyAboveGoalPercentage')).not.toBeInTheDocument(); - expect(queryByTestId('MonthlyGoalTypographyAboveGoal')).not.toBeInTheDocument(); - }); + it('props', () => { + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('MonthlyGoalTypographyGoal').textContent).toEqual( + '€1,000', + ); + expect( + getByTestId('MonthlyGoalTypographyReceivedPercentage').textContent, + ).toEqual('50%'); + expect(getByTestId('MonthlyGoalTypographyReceived').textContent).toEqual( + '€500', + ); + expect( + getByTestId('MonthlyGoalTypographyPledgedPercentage').textContent, + ).toEqual('75%'); + expect(getByTestId('MonthlyGoalTypographyPledged').textContent).toEqual( + '€750', + ); + expect( + getByTestId('MonthlyGoalTypographyBelowGoalPercentage').textContent, + ).toEqual('25%'); + expect(getByTestId('MonthlyGoalTypographyBelowGoal').textContent).toEqual( + '€250', + ); + expect( + queryByTestId('MonthlyGoalTypographyAboveGoalPercentage'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('MonthlyGoalTypographyAboveGoal'), + ).not.toBeInTheDocument(); + }); - it('props above goal', () => { - const { getByTestId, queryByTestId } = render( - , - ); - expect(getByTestId('MonthlyGoalTypographyReceivedPercentage').textContent).toEqual('500%'); - expect(getByTestId('MonthlyGoalTypographyPledgedPercentage').textContent).toEqual('750%'); - expect(getByTestId('MonthlyGoalTypographyAboveGoalPercentage').textContent).toEqual('650%'); - expect(getByTestId('MonthlyGoalTypographyAboveGoal').textContent).toEqual('€6,501'); - expect(queryByTestId('MonthlyGoalTypographyBelowGoalPercentage')).not.toBeInTheDocument(); - expect(queryByTestId('MonthlyGoalTypographyBelowGoal')).not.toBeInTheDocument(); - }); + it('props above goal', () => { + const { getByTestId, queryByTestId } = render( + , + ); + expect( + getByTestId('MonthlyGoalTypographyReceivedPercentage').textContent, + ).toEqual('500%'); + expect( + getByTestId('MonthlyGoalTypographyPledgedPercentage').textContent, + ).toEqual('750%'); + expect( + getByTestId('MonthlyGoalTypographyAboveGoalPercentage').textContent, + ).toEqual('650%'); + expect(getByTestId('MonthlyGoalTypographyAboveGoal').textContent).toEqual( + '€6,501', + ); + expect( + queryByTestId('MonthlyGoalTypographyBelowGoalPercentage'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('MonthlyGoalTypographyBelowGoal'), + ).not.toBeInTheDocument(); + }); - describe('mobile', () => { - beforeEach(() => { - matchMediaMock({ width: '599px' }); - }); + describe('mobile', () => { + beforeEach(() => { + matchMediaMock({ width: '599px' }); + }); - it('default', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('MonthlyGoalTypographyGoalMobile').textContent).toEqual('$0'); - expect(queryByTestId('MonthlyGoalTypographyGoal')).not.toBeInTheDocument(); - expect(queryByTestId('MonthlyGoalTypographyAboveGoalPercentage')).not.toBeInTheDocument(); - expect(queryByTestId('MonthlyGoalTypographyAboveGoal')).not.toBeInTheDocument(); - }); + it('default', () => { + const { getByTestId, queryByTestId } = render(); + expect( + getByTestId('MonthlyGoalTypographyGoalMobile').textContent, + ).toEqual('$0'); + expect( + queryByTestId('MonthlyGoalTypographyGoal'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('MonthlyGoalTypographyAboveGoalPercentage'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('MonthlyGoalTypographyAboveGoal'), + ).not.toBeInTheDocument(); + }); - it('props', () => { - const { getByTestId } = render( - , - ); - expect(getByTestId('MonthlyGoalTypographyGoalMobile').textContent).toEqual('€1,000'); - }); + it('props', () => { + const { getByTestId } = render( + , + ); + expect( + getByTestId('MonthlyGoalTypographyGoalMobile').textContent, + ).toEqual('€1,000'); }); + }); }); diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx index c7574c07f9..da3c689ac2 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx @@ -1,5 +1,13 @@ import React, { ReactElement } from 'react'; -import { Typography, makeStyles, Theme, Grid, CardContent, Box, Hidden } from '@material-ui/core'; +import { + Typography, + makeStyles, + Theme, + Grid, + CardContent, + Box, + Hidden, +} from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; import { useTranslation } from 'react-i18next'; import { currencyFormat, percentageFormat } from '../../../lib/intlFormat'; @@ -8,150 +16,205 @@ import AnimatedBox from '../../AnimatedBox'; import StyledProgress from '../../StyledProgress'; const useStyles = makeStyles((_theme: Theme) => ({ - received: { - background: 'linear-gradient(180deg, #FFE67C 0%, #FFCF07 100%)', - }, - pledged: { - border: '5px solid #FFCF07', - }, - goal: { - border: '2px solid #999999', - }, - indicator: { - display: 'inline-block', - borderRadius: '18px', - width: '18px', - height: '18px', - marginRight: '5px', - marginBottom: '-3px', - }, + received: { + background: 'linear-gradient(180deg, #FFE67C 0%, #FFCF07 100%)', + }, + pledged: { + border: '5px solid #FFCF07', + }, + goal: { + border: '2px solid #999999', + }, + indicator: { + display: 'inline-block', + borderRadius: '18px', + width: '18px', + height: '18px', + marginRight: '5px', + marginBottom: '-3px', + }, })); interface Props { - loading?: boolean; - goal?: number; - received?: number; - pledged?: number; - currencyCode?: string; + loading?: boolean; + goal?: number; + received?: number; + pledged?: number; + currencyCode?: string; } -const MonthlyGoal = ({ loading, goal, received, pledged, currencyCode = 'USD' }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); - const receivedPercentage = received / goal; - const pledgedPercentage = pledged / goal; - const belowGoal = goal - pledged; - const belowGoalPercentage = belowGoal / goal; +const MonthlyGoal = ({ + loading, + goal, + received, + pledged, + currencyCode = 'USD', +}: Props): ReactElement => { + const classes = useStyles(); + const { t } = useTranslation(); + const receivedPercentage = received / goal; + const pledgedPercentage = pledged / goal; + const belowGoal = goal - pledged; + const belowGoalPercentage = belowGoal / goal; - return ( - <> - - - - - {t('Monthly Goal')} - - - {!loading && currencyFormat(goal, currencyCode)} - - - - - + return ( + <> + + + + + {t('Monthly Goal')} + + + {!loading && currencyFormat(goal, currencyCode)} + + - - - - - - - -
- {t('Goal')} - - - {loading ? : currencyFormat(goal, currencyCode)} - - - - - -
- {t('Gifts Started')} - - - {loading ? ( - - ) : isNaN(receivedPercentage) ? ( - '-' - ) : ( - percentageFormat(receivedPercentage) - )} - - - {loading ? : currencyFormat(received, currencyCode)} - - - - -
- {t('Commitments')} - - - {loading ? ( - - ) : isNaN(pledgedPercentage) ? ( - '-' - ) : ( - percentageFormat(pledgedPercentage) - )} - - - {loading ? : currencyFormat(pledged, currencyCode)} - - - - {!isNaN(belowGoal) && belowGoal > 0 ? ( - - - {t('Below Goal')} - - - {percentageFormat(belowGoalPercentage)} - - - {currencyFormat(belowGoal, currencyCode)} - - - ) : ( - - - {t('Above Goal')} - - - {loading ? ( - - ) : isNaN(belowGoalPercentage) ? ( - '-' - ) : ( - percentageFormat(-belowGoalPercentage) - )} - - - {loading ? ( - - ) : ( - currencyFormat(-belowGoal, currencyCode) - )} - - - )} - - - - - - ); + + + + + + + + + + +
+ {t('Goal')} + + + {loading ? ( + + ) : ( + currencyFormat(goal, currencyCode) + )} + + + + + +
+ {t('Gifts Started')} + + + {loading ? ( + + ) : isNaN(receivedPercentage) ? ( + '-' + ) : ( + percentageFormat(receivedPercentage) + )} + + + {loading ? ( + + ) : ( + currencyFormat(received, currencyCode) + )} + + + + +
+ {t('Commitments')} + + + {loading ? ( + + ) : isNaN(pledgedPercentage) ? ( + '-' + ) : ( + percentageFormat(pledgedPercentage) + )} + + + {loading ? ( + + ) : ( + currencyFormat(pledged, currencyCode) + )} + + + + {!isNaN(belowGoal) && belowGoal > 0 ? ( + + + {t('Below Goal')} + + + {percentageFormat(belowGoalPercentage)} + + + {currencyFormat(belowGoal, currencyCode)} + + + ) : ( + + + {t('Above Goal')} + + + {loading ? ( + + ) : isNaN(belowGoalPercentage) ? ( + '-' + ) : ( + percentageFormat(-belowGoalPercentage) + )} + + + {loading ? ( + + ) : ( + currencyFormat(-belowGoal, currencyCode) + )} + + + )} + + + + + + ); }; export default MonthlyGoal; diff --git a/src/components/Dashboard/ThisWeek/Appeals/Appeals.stories.tsx b/src/components/Dashboard/ThisWeek/Appeals/Appeals.stories.tsx index 361f1bc794..5a614433b5 100644 --- a/src/components/Dashboard/ThisWeek/Appeals/Appeals.stories.tsx +++ b/src/components/Dashboard/ThisWeek/Appeals/Appeals.stories.tsx @@ -4,37 +4,37 @@ import { GetThisWeekQuery_accountList_primaryAppeal } from '../../../../../types import Appeals from '.'; export default { - title: 'Dashboard/ThisWeek/Appeals', + title: 'Dashboard/ThisWeek/Appeals', }; export const Default = (): ReactElement => { - const appeal: GetThisWeekQuery_accountList_primaryAppeal = { - id: 'appeal', - name: '2020 End of Year Ask With Really long Appeal Name!', - amount: 1000, - pledgesAmountTotal: 750, - pledgesAmountProcessed: 500, - amountCurrency: 'GBP', - }; - return ( - - - - ); + const appeal: GetThisWeekQuery_accountList_primaryAppeal = { + id: 'appeal', + name: '2020 End of Year Ask With Really long Appeal Name!', + amount: 1000, + pledgesAmountTotal: 750, + pledgesAmountProcessed: 500, + amountCurrency: 'GBP', + }; + return ( + + + + ); }; export const Empty = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; diff --git a/src/components/Dashboard/ThisWeek/Appeals/Appeals.test.tsx b/src/components/Dashboard/ThisWeek/Appeals/Appeals.test.tsx index 8d7e8f9c04..6bc68cc4bf 100644 --- a/src/components/Dashboard/ThisWeek/Appeals/Appeals.test.tsx +++ b/src/components/Dashboard/ThisWeek/Appeals/Appeals.test.tsx @@ -3,44 +3,65 @@ import { render } from '@testing-library/react'; import Appeals from '.'; describe('Appeals', () => { - it('default', () => { - const { getByTestId } = render(); - expect(getByTestId('AppealsCardContentEmpty')).toBeInTheDocument(); - }); + it('default', () => { + const { getByTestId } = render(); + expect(getByTestId('AppealsCardContentEmpty')).toBeInTheDocument(); + }); - it('loading', () => { - const { getByTestId, getByRole } = render(); - expect(getByTestId('AppealsBoxName').children[0].className).toContain('MuiSkeleton-root'); - expect(getByTestId('AppealsBoxAmount').children[0].className).toContain('MuiSkeleton-root'); - expect(getByTestId('AppealsTypographyPledgesAmountProcessedPercentage').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('AppealsTypographyPledgesAmountProcessed').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('AppealsTypographyPledgesAmountTotalPercentage').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('AppealsTypographyPledgesAmountTotal').children[0].className).toContain('MuiSkeleton-root'); - expect(getByRole('link', { name: 'View All' })).toHaveAttribute('href', 'https://stage.mpdx.org/tools/appeals'); - }); + it('loading', () => { + const { getByTestId, getByRole } = render(); + expect(getByTestId('AppealsBoxName').children[0].className).toContain( + 'MuiSkeleton-root', + ); + expect(getByTestId('AppealsBoxAmount').children[0].className).toContain( + 'MuiSkeleton-root', + ); + expect( + getByTestId('AppealsTypographyPledgesAmountProcessedPercentage') + .children[0].className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('AppealsTypographyPledgesAmountProcessed').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('AppealsTypographyPledgesAmountTotalPercentage').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('AppealsTypographyPledgesAmountTotal').children[0].className, + ).toContain('MuiSkeleton-root'); + expect(getByRole('link', { name: 'View All' })).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/tools/appeals', + ); + }); - it('props', () => { - const appeal = { - id: 'appealId', - name: 'My Appeal', - amount: 4999.99, - pledgesAmountTotal: 2499.99, - pledgesAmountProcessed: 999.99, - amountCurrency: 'EUR', - }; - const { getByTestId } = render(); + it('props', () => { + const appeal = { + id: 'appealId', + name: 'My Appeal', + amount: 4999.99, + pledgesAmountTotal: 2499.99, + pledgesAmountProcessed: 999.99, + amountCurrency: 'EUR', + }; + const { getByTestId } = render(); - expect(getByTestId('AppealsBoxName').textContent).toEqual('My Appeal'); - expect(getByTestId('AppealsBoxAmount').textContent).toEqual('€5,000'); - expect(getByTestId('AppealsTypographyPledgesAmountProcessedPercentage').textContent).toEqual('20%'); - expect(getByTestId('AppealsTypographyPledgesAmountProcessed').textContent).toEqual('€1,000'); - expect(getByTestId('AppealsTypographyPledgesAmountTotalPercentage').textContent).toEqual('50%'); - expect(getByTestId('AppealsTypographyPledgesAmountTotal').textContent).toEqual('€2,500'); - }); + expect(getByTestId('AppealsBoxName').textContent).toEqual('My Appeal'); + expect(getByTestId('AppealsBoxAmount').textContent).toEqual('€5,000'); + expect( + getByTestId('AppealsTypographyPledgesAmountProcessedPercentage') + .textContent, + ).toEqual('20%'); + expect( + getByTestId('AppealsTypographyPledgesAmountProcessed').textContent, + ).toEqual('€1,000'); + expect( + getByTestId('AppealsTypographyPledgesAmountTotalPercentage').textContent, + ).toEqual('50%'); + expect( + getByTestId('AppealsTypographyPledgesAmountTotal').textContent, + ).toEqual('€2,500'); + }); }); diff --git a/src/components/Dashboard/ThisWeek/Appeals/Appeals.tsx b/src/components/Dashboard/ThisWeek/Appeals/Appeals.tsx index b7962c7aab..556f6b3a48 100644 --- a/src/components/Dashboard/ThisWeek/Appeals/Appeals.tsx +++ b/src/components/Dashboard/ThisWeek/Appeals/Appeals.tsx @@ -1,14 +1,14 @@ import React, { ReactElement } from 'react'; import { - makeStyles, - Theme, - CardHeader, - CardActions, - Button, - CardContent, - Typography, - Grid, - Box, + makeStyles, + Theme, + CardHeader, + CardActions, + Button, + CardContent, + Typography, + Grid, + Box, } from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; import { motion } from 'framer-motion'; @@ -21,183 +21,224 @@ import HandoffLink from '../../../HandoffLink'; import illustration13 from '../../../../images/drawkit/grape/drawkit-grape-pack-illustration-13.svg'; const useStyles = makeStyles((theme: Theme) => ({ - div: { - flex: 1, - display: 'flex', - flexDirection: 'column', - overflow: 'auto', + div: { + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'auto', + }, + list: { + flex: 1, + padding: 0, + overflow: 'auto', + }, + card: { + display: 'flex', + flexDirection: 'column', + height: '322px', + [theme.breakpoints.down('xs')]: { + height: 'auto', }, - list: { - flex: 1, - padding: 0, - overflow: 'auto', - }, - card: { - display: 'flex', - flexDirection: 'column', - height: '322px', - [theme.breakpoints.down('xs')]: { - height: 'auto', - }, - }, - cardContent: { - display: 'flex', - flex: 1, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - padding: theme.spacing(2), - }, - cardContentExpanded: { - padding: theme.spacing(0, 2), - [theme.breakpoints.down('xs')]: { - padding: theme.spacing(2), - }, - }, - img: { - height: '150px', - marginBottom: theme.spacing(2), - }, - pledgesAmountProcessed: { - background: 'linear-gradient(180deg, #FFE67C 0%, #FFCF07 100%)', - }, - pledgesAmountTotal: { - border: '5px solid #FFCF07', - }, - indicator: { - display: 'inline-block', - borderRadius: '18px', - width: '18px', - height: '18px', - marginRight: '5px', - marginBottom: '-3px', - }, - titleContainer: { - width: '100%', - }, - title: { - display: 'flex', - marginBottom: theme.spacing(1), - whiteSpace: 'nowrap', - }, - titleContent: { - flexGrow: 1, - textOverflow: 'ellipsis', - overflow: 'hidden', + }, + cardContent: { + display: 'flex', + flex: 1, + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: theme.spacing(2), + }, + cardContentExpanded: { + padding: theme.spacing(0, 2), + [theme.breakpoints.down('xs')]: { + padding: theme.spacing(2), }, + }, + img: { + height: '150px', + marginBottom: theme.spacing(2), + }, + pledgesAmountProcessed: { + background: 'linear-gradient(180deg, #FFE67C 0%, #FFCF07 100%)', + }, + pledgesAmountTotal: { + border: '5px solid #FFCF07', + }, + indicator: { + display: 'inline-block', + borderRadius: '18px', + width: '18px', + height: '18px', + marginRight: '5px', + marginBottom: '-3px', + }, + titleContainer: { + width: '100%', + }, + title: { + display: 'flex', + marginBottom: theme.spacing(1), + whiteSpace: 'nowrap', + }, + titleContent: { + flexGrow: 1, + textOverflow: 'ellipsis', + overflow: 'hidden', + }, })); interface Props { - loading?: boolean; - appeal?: GetThisWeekQuery_accountList_primaryAppeal; + loading?: boolean; + appeal?: GetThisWeekQuery_accountList_primaryAppeal; } const Appeals = ({ loading, appeal }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); - const pledgesAmountProcessedPercentage = (appeal?.pledgesAmountProcessed || 0) / (appeal?.amount || 0); - const pledgesAmountTotalPercentage = (appeal?.pledgesAmountTotal || 0) / (appeal?.amount || 0); + const classes = useStyles(); + const { t } = useTranslation(); + const pledgesAmountProcessedPercentage = + (appeal?.pledgesAmountProcessed || 0) / (appeal?.amount || 0); + const pledgesAmountTotalPercentage = + (appeal?.pledgesAmountTotal || 0) / (appeal?.amount || 0); - return ( - - - {!loading && !appeal && ( - - - empty - {t('No primary appeal to show.')} - - + return ( + + + {!loading && !appeal && ( + + + empty + {t('No primary appeal to show.')} + + + )} + {(loading || appeal) && ( + + + + + - - - - - {loading ? : appeal.name} - - - {loading ? ( - - ) : ( - currencyFormat(appeal.amount, appeal.amountCurrency) - )} - - - - - - - -
- {t('Gifts Received')} - - - {loading ? ( - - ) : ( - percentageFormat(pledgesAmountProcessedPercentage) - )} - - - {loading ? ( - - ) : ( - currencyFormat(appeal.pledgesAmountProcessed, appeal.amountCurrency) - )} - - - - -
- {t('Commitments')} - - - {loading ? ( - - ) : ( - percentageFormat(pledgesAmountTotalPercentage) - )} - - - {loading ? ( - - ) : ( - currencyFormat(appeal.pledgesAmountTotal, appeal.amountCurrency) - )} - - - - - - - - - - - )} - - ); + {loading ? ( + + ) : ( + appeal.name + )} + + + {loading ? ( + + ) : ( + currencyFormat(appeal.amount, appeal.amountCurrency) + )} + + + + + + + +
+ {t('Gifts Received')} + + + {loading ? ( + + ) : ( + percentageFormat(pledgesAmountProcessedPercentage) + )} + + + {loading ? ( + + ) : ( + currencyFormat( + appeal.pledgesAmountProcessed, + appeal.amountCurrency, + ) + )} + + + + +
+ {t('Commitments')} + + + {loading ? ( + + ) : ( + percentageFormat(pledgesAmountTotalPercentage) + )} + + + {loading ? ( + + ) : ( + currencyFormat( + appeal.pledgesAmountTotal, + appeal.amountCurrency, + ) + )} + + + + + + + + + + + )} + + ); }; export default Appeals; diff --git a/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.stories.tsx b/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.stories.tsx index 3ce9aa9073..72cd91c9da 100644 --- a/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.stories.tsx +++ b/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.stories.tsx @@ -4,47 +4,47 @@ import { GetThisWeekQuery_latePledgeContacts } from '../../../../../types/GetThi import LateCommitments from '.'; export default { - title: 'Dashboard/ThisWeek/LateCommitments', + title: 'Dashboard/ThisWeek/LateCommitments', }; export const Default = (): ReactElement => { - const contact = { - id: 'contact', - name: 'Smith, Sarah', - lateAt: '2012-10-01', - }; + const contact = { + id: 'contact', + name: 'Smith, Sarah', + lateAt: '2012-10-01', + }; - const latePledgeContacts: GetThisWeekQuery_latePledgeContacts = { - nodes: [ - { ...contact, id: 'contact_1' }, - { ...contact, id: 'contact_2' }, - { ...contact, id: 'contact_3' }, - ], - totalCount: 5, - }; - return ( - - - - ); + const latePledgeContacts: GetThisWeekQuery_latePledgeContacts = { + nodes: [ + { ...contact, id: 'contact_1' }, + { ...contact, id: 'contact_2' }, + { ...contact, id: 'contact_3' }, + ], + totalCount: 5, + }; + return ( + + + + ); }; export const Empty = (): ReactElement => { - const latePledgeContacts: GetThisWeekQuery_latePledgeContacts = { - nodes: [], - totalCount: 0, - }; - return ( - - - - ); + const latePledgeContacts: GetThisWeekQuery_latePledgeContacts = { + nodes: [], + totalCount: 0, + }; + return ( + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; diff --git a/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.test.tsx b/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.test.tsx index f045bb1844..69e31aa962 100644 --- a/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.test.tsx +++ b/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.test.tsx @@ -4,69 +4,99 @@ import { render } from '../../../../../__tests__/util/testingLibraryReactMock'; import LateCommitments from '.'; describe('LateCommitments', () => { - it('default', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('LateCommitmentsCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('LateCommitmentsDivLoading')).not.toBeInTheDocument(); - expect(queryByTestId('LateCommitmentsListContacts')).not.toBeInTheDocument(); - }); + it('default', () => { + const { getByTestId, queryByTestId } = render(); + expect(getByTestId('LateCommitmentsCardContentEmpty')).toBeInTheDocument(); + expect(queryByTestId('LateCommitmentsDivLoading')).not.toBeInTheDocument(); + expect( + queryByTestId('LateCommitmentsListContacts'), + ).not.toBeInTheDocument(); + }); - it('loading', () => { - const { getByTestId, queryByTestId } = render(); - expect(queryByTestId('LateCommitmentsCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('LateCommitmentsDivLoading')).toBeInTheDocument(); - expect(queryByTestId('LateCommitmentsListContacts')).not.toBeInTheDocument(); - }); + it('loading', () => { + const { getByTestId, queryByTestId } = render(); + expect( + queryByTestId('LateCommitmentsCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('LateCommitmentsDivLoading')).toBeInTheDocument(); + expect( + queryByTestId('LateCommitmentsListContacts'), + ).not.toBeInTheDocument(); + }); - it('empty', () => { - const latePledgeContacts = { - nodes: [], - totalCount: 0, - }; - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('LateCommitmentsCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('LateCommitmentsDivLoading')).not.toBeInTheDocument(); - expect(queryByTestId('LateCommitmentsListContacts')).not.toBeInTheDocument(); - }); + it('empty', () => { + const latePledgeContacts = { + nodes: [], + totalCount: 0, + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('LateCommitmentsCardContentEmpty')).toBeInTheDocument(); + expect(queryByTestId('LateCommitmentsDivLoading')).not.toBeInTheDocument(); + expect( + queryByTestId('LateCommitmentsListContacts'), + ).not.toBeInTheDocument(); + }); - describe('MockDate', () => { - beforeEach(() => { - MockDate.set(new Date(2020, 1, 1)); - }); - afterEach(() => { - MockDate.reset(); - }); - it('props', () => { - const latePledgeContacts = { - nodes: [ - { - id: 'contact1', - name: 'Smith, Sarah', - lateAt: '2012-10-01', - }, - { - id: 'contact2', - name: 'Smith, John', - lateAt: '2015-12-01', - }, - ], - totalCount: 1595, - }; - const { getByTestId, queryByTestId } = render(); - expect(queryByTestId('LateCommitmentsCardContentEmpty')).not.toBeInTheDocument(); - expect(queryByTestId('LateCommitmentsDivLoading')).not.toBeInTheDocument(); - const buttonElement = getByTestId('LateCommitmentsButtonViewAll'); - expect(buttonElement.textContent).toEqual('View All (1,595)'); - expect(buttonElement).toHaveAttribute( - 'href', - 'https://stage.mpdx.org/contacts?filters=%7B%22late_at%22%3A%221970-01-01..2020-02-01%22%2C%22status%22%3A%22Partner%20-%20Financial%22%7D', - ); - const contact1Element = getByTestId('LateCommitmentsListItemContact-contact1'); - expect(contact1Element).toHaveAttribute('href', 'https://stage.mpdx.org/contacts/contact1'); - expect(contact1Element.textContent).toEqual('Smith, SarahTheir gift is 2,679 days late.'); - const contact2Element = getByTestId('LateCommitmentsListItemContact-contact2'); - expect(contact2Element).toHaveAttribute('href', 'https://stage.mpdx.org/contacts/contact2'); - expect(contact2Element.textContent).toEqual('Smith, JohnTheir gift is 1,523 days late.'); - }); + describe('MockDate', () => { + beforeEach(() => { + MockDate.set(new Date(2020, 1, 1)); + }); + afterEach(() => { + MockDate.reset(); + }); + it('props', () => { + const latePledgeContacts = { + nodes: [ + { + id: 'contact1', + name: 'Smith, Sarah', + lateAt: '2012-10-01', + }, + { + id: 'contact2', + name: 'Smith, John', + lateAt: '2015-12-01', + }, + ], + totalCount: 1595, + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect( + queryByTestId('LateCommitmentsCardContentEmpty'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('LateCommitmentsDivLoading'), + ).not.toBeInTheDocument(); + const buttonElement = getByTestId('LateCommitmentsButtonViewAll'); + expect(buttonElement.textContent).toEqual('View All (1,595)'); + expect(buttonElement).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts?filters=%7B%22late_at%22%3A%221970-01-01..2020-02-01%22%2C%22status%22%3A%22Partner%20-%20Financial%22%7D', + ); + const contact1Element = getByTestId( + 'LateCommitmentsListItemContact-contact1', + ); + expect(contact1Element).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts/contact1', + ); + expect(contact1Element.textContent).toEqual( + 'Smith, SarahTheir gift is 2,679 days late.', + ); + const contact2Element = getByTestId( + 'LateCommitmentsListItemContact-contact2', + ); + expect(contact2Element).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts/contact2', + ); + expect(contact2Element.textContent).toEqual( + 'Smith, JohnTheir gift is 1,523 days late.', + ); }); + }); }); diff --git a/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.tsx b/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.tsx index 8f1311b486..8dff5bdaec 100644 --- a/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.tsx +++ b/src/components/Dashboard/ThisWeek/LateCommitments/LateCommitments.tsx @@ -1,14 +1,14 @@ import React, { ReactElement } from 'react'; import { - makeStyles, - Theme, - CardHeader, - CardActions, - Button, - List, - ListItem, - ListItemText, - CardContent, + makeStyles, + Theme, + CardHeader, + CardActions, + Button, + List, + ListItem, + ListItemText, + CardContent, } from '@material-ui/core'; import moment from 'moment'; import { Skeleton } from '@material-ui/lab'; @@ -21,136 +21,155 @@ import HandoffLink from '../../../HandoffLink'; import illustration14 from '../../../../images/drawkit/grape/drawkit-grape-pack-illustration-14.svg'; const useStyles = makeStyles((theme: Theme) => ({ - div: { - flex: 1, - display: 'flex', - flexDirection: 'column', - overflow: 'auto', - }, - list: { - flex: 1, - padding: 0, - overflow: 'auto', - }, - card: { - display: 'flex', - flexDirection: 'column', - height: '322px', - [theme.breakpoints.down('xs')]: { - height: 'auto', - }, - }, - cardContent: { - padding: theme.spacing(2), - display: 'flex', - flex: 1, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }, - img: { - height: '150px', - marginBottom: theme.spacing(2), + div: { + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'auto', + }, + list: { + flex: 1, + padding: 0, + overflow: 'auto', + }, + card: { + display: 'flex', + flexDirection: 'column', + height: '322px', + [theme.breakpoints.down('xs')]: { + height: 'auto', }, + }, + cardContent: { + padding: theme.spacing(2), + display: 'flex', + flex: 1, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, + img: { + height: '150px', + marginBottom: theme.spacing(2), + }, })); interface Props { - loading?: boolean; - latePledgeContacts?: GetThisWeekQuery_latePledgeContacts; + loading?: boolean; + latePledgeContacts?: GetThisWeekQuery_latePledgeContacts; } -const LateCommitments = ({ loading, latePledgeContacts }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); +const LateCommitments = ({ + loading, + latePledgeContacts, +}: Props): ReactElement => { + const classes = useStyles(); + const { t } = useTranslation(); - return ( - - - {loading && ( - - - {[0, 1, 2].map((index) => ( - - } - secondary={} - /> - - ))} - - - - - - )} - {!loading && ( - + + {loading && ( + + + {[0, 1, 2].map((index) => ( + + } + secondary={} + /> + + ))} + + + + + + )} + {!loading && ( + + {(!latePledgeContacts || latePledgeContacts.nodes.length === 0) && ( + + empty + {t('No late commitments to show.')} + + )} + {latePledgeContacts && latePledgeContacts.nodes.length > 0 && ( + <> + + {latePledgeContacts.nodes.map((contact) => { + const count = moment().diff(moment(contact.lateAt), 'days'); + return ( + + + + + + ); + })} + + + - {(!latePledgeContacts || latePledgeContacts.nodes.length === 0) && ( - - empty - {t('No late commitments to show.')} - - )} - {latePledgeContacts && latePledgeContacts.nodes.length > 0 && ( - <> - - {latePledgeContacts.nodes.map((contact) => { - const count = moment().diff(moment(contact.lateAt), 'days'); - return ( - - - - - - ); - })} - - - - - - - - )} - - )} - - ); + + + + + )} + + )} + + ); }; export default LateCommitments; diff --git a/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.stories.tsx b/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.stories.tsx index 46c99b414d..0694365b2e 100644 --- a/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.stories.tsx +++ b/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.stories.tsx @@ -2,128 +2,128 @@ import React, { ReactElement } from 'react'; import { Box } from '@material-ui/core'; import { MockedProvider } from '@apollo/client/testing'; import { - GetThisWeekQuery_prayerRequestTasks, - GetThisWeekQuery_reportsPeopleWithBirthdays, - GetThisWeekQuery_reportsPeopleWithAnniversaries, + GetThisWeekQuery_prayerRequestTasks, + GetThisWeekQuery_reportsPeopleWithBirthdays, + GetThisWeekQuery_reportsPeopleWithAnniversaries, } from '../../../../../types/GetThisWeekQuery'; import { ActivityTypeEnum } from '../../../../../types/globalTypes'; import PartnerCare from '.'; export default { - title: 'Dashboard/ThisWeek/PartnerCare', + title: 'Dashboard/ThisWeek/PartnerCare', }; export const Default = (): ReactElement => { - const task = { - id: 'task', - subject: 'the quick brown fox jumps over the lazy dog', - activityType: ActivityTypeEnum.PRAYER_REQUEST, - contacts: { nodes: [{ name: 'Roger Smith' }, { name: 'Sarah Smith' }] }, - startAt: null, - completedAt: null, - }; - const personWithBirthday = { - id: 'person', - birthdayDay: 1, - birthdayMonth: 1, - firstName: 'John', - lastName: 'Doe', - parentContact: { - id: 'contact', - }, - }; - const personWithAnniversary = { - id: 'person', - anniversaryDay: 5, - anniversaryMonth: 10, - parentContact: { - id: 'contact', - name: 'John and Sarah, Doe', - }, - }; - const prayerRequestTasks: GetThisWeekQuery_prayerRequestTasks = { - nodes: [ - { ...task, id: 'task_4' }, - { ...task, id: 'task_5' }, - { ...task, id: 'task_6' }, + const task = { + id: 'task', + subject: 'the quick brown fox jumps over the lazy dog', + activityType: ActivityTypeEnum.PRAYER_REQUEST, + contacts: { nodes: [{ name: 'Roger Smith' }, { name: 'Sarah Smith' }] }, + startAt: null, + completedAt: null, + }; + const personWithBirthday = { + id: 'person', + birthdayDay: 1, + birthdayMonth: 1, + firstName: 'John', + lastName: 'Doe', + parentContact: { + id: 'contact', + }, + }; + const personWithAnniversary = { + id: 'person', + anniversaryDay: 5, + anniversaryMonth: 10, + parentContact: { + id: 'contact', + name: 'John and Sarah, Doe', + }, + }; + const prayerRequestTasks: GetThisWeekQuery_prayerRequestTasks = { + nodes: [ + { ...task, id: 'task_4' }, + { ...task, id: 'task_5' }, + { ...task, id: 'task_6' }, + ], + totalCount: 80, + }; + const reportsPeopleWithBirthdays: GetThisWeekQuery_reportsPeopleWithBirthdays = { + periods: [ + { + people: [ + { ...personWithBirthday, id: 'person_1' }, + { ...personWithBirthday, id: 'person_2' }, ], - totalCount: 80, - }; - const reportsPeopleWithBirthdays: GetThisWeekQuery_reportsPeopleWithBirthdays = { - periods: [ - { - people: [ - { ...personWithBirthday, id: 'person_1' }, - { ...personWithBirthday, id: 'person_2' }, - ], - }, + }, + ], + }; + const reportsPeopleWithAnniversaries: GetThisWeekQuery_reportsPeopleWithAnniversaries = { + periods: [ + { + people: [ + { ...personWithAnniversary, id: 'person_3' }, + { ...personWithAnniversary, id: 'person_4' }, ], - }; - const reportsPeopleWithAnniversaries: GetThisWeekQuery_reportsPeopleWithAnniversaries = { - periods: [ - { - people: [ - { ...personWithAnniversary, id: 'person_3' }, - { ...personWithAnniversary, id: 'person_4' }, - ], - }, - ], - }; - return ( - - - - - - ); + }, + ], + }; + return ( + + + + + + ); }; export const Empty = (): ReactElement => { - const prayerRequestTasks: GetThisWeekQuery_prayerRequestTasks = { - nodes: [], - totalCount: 0, - }; - const reportsPeopleWithBirthdays: GetThisWeekQuery_reportsPeopleWithBirthdays = { - periods: [ - { - people: [], - }, - ], - }; - const reportsPeopleWithAnniversaries: GetThisWeekQuery_reportsPeopleWithAnniversaries = { - periods: [ - { - people: [], - }, - ], - }; - return ( - - - - - - ); + const prayerRequestTasks: GetThisWeekQuery_prayerRequestTasks = { + nodes: [], + totalCount: 0, + }; + const reportsPeopleWithBirthdays: GetThisWeekQuery_reportsPeopleWithBirthdays = { + periods: [ + { + people: [], + }, + ], + }; + const reportsPeopleWithAnniversaries: GetThisWeekQuery_reportsPeopleWithAnniversaries = { + periods: [ + { + people: [], + }, + ], + }; + return ( + + + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - - - ); + return ( + + + + + + ); }; diff --git a/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.test.tsx b/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.test.tsx index ee6e8a5e03..3a1336d1be 100644 --- a/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.test.tsx +++ b/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.test.tsx @@ -1,161 +1,214 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; -import { render, fireEvent } from '../../../../../__tests__/util/testingLibraryReactMock'; +import { + render, + fireEvent, +} from '../../../../../__tests__/util/testingLibraryReactMock'; import { ActivityTypeEnum } from '../../../../../types/globalTypes'; import { GetThisWeekQuery_prayerRequestTasks } from '../../../../../types/GetThisWeekQuery'; import { useApp } from '../../../App'; import PartnerCare from '.'; jest.mock('../../../App', () => ({ - useApp: jest.fn(), + useApp: jest.fn(), })); const openTaskDrawer = jest.fn(); beforeEach(() => { - (useApp as jest.Mock).mockReturnValue({ - openTaskDrawer, - }); + (useApp as jest.Mock).mockReturnValue({ + openTaskDrawer, + }); }); describe('PartnerCare', () => { - it('default', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('PartnerCarePrayerCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('PartnerCareCelebrationCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('PartnerCareTabPrayer').textContent).toEqual('Prayer (0)'); - const CelebrationsTab = getByTestId('PartnerCareTabCelebrations'); - expect(CelebrationsTab.textContent).toEqual('Celebrations (0)'); - fireEvent.click(CelebrationsTab); - expect(getByTestId('PartnerCareCelebrationCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('PartnerCarePrayerCardContentEmpty')).not.toBeInTheDocument(); - }); + it('default', () => { + const { getByTestId, queryByTestId } = render( + , + ); + expect( + getByTestId('PartnerCarePrayerCardContentEmpty'), + ).toBeInTheDocument(); + expect( + queryByTestId('PartnerCareCelebrationCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('PartnerCareTabPrayer').textContent).toEqual( + 'Prayer (0)', + ); + const CelebrationsTab = getByTestId('PartnerCareTabCelebrations'); + expect(CelebrationsTab.textContent).toEqual('Celebrations (0)'); + fireEvent.click(CelebrationsTab); + expect( + getByTestId('PartnerCareCelebrationCardContentEmpty'), + ).toBeInTheDocument(); + expect( + queryByTestId('PartnerCarePrayerCardContentEmpty'), + ).not.toBeInTheDocument(); + }); - it('loading', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('PartnerCarePrayerListLoading')).toBeInTheDocument(); - expect(queryByTestId('PartnerCareCelebrationListLoading')).not.toBeInTheDocument(); - fireEvent.click(getByTestId('PartnerCareTabCelebrations')); - expect(getByTestId('PartnerCareCelebrationListLoading')).toBeInTheDocument(); - expect(queryByTestId('PartnerCarePrayerListLoading')).not.toBeInTheDocument(); - }); + it('loading', () => { + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('PartnerCarePrayerListLoading')).toBeInTheDocument(); + expect( + queryByTestId('PartnerCareCelebrationListLoading'), + ).not.toBeInTheDocument(); + fireEvent.click(getByTestId('PartnerCareTabCelebrations')); + expect( + getByTestId('PartnerCareCelebrationListLoading'), + ).toBeInTheDocument(); + expect( + queryByTestId('PartnerCarePrayerListLoading'), + ).not.toBeInTheDocument(); + }); - it('empty', () => { - const prayerRequestTasks = { - nodes: [], - totalCount: 0, - }; - const reportsPeopleWithBirthdays = { - periods: [{ people: [] }], - totalCount: 0, - }; - const reportsPeopleWithAnniversaries = { - periods: [{ people: [] }], - totalCount: 0, - }; - const { getByTestId, queryByTestId } = render( - , - ); - expect(getByTestId('PartnerCarePrayerCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('PartnerCareCelebrationCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('PartnerCareTabPrayer').textContent).toEqual('Prayer (0)'); - const CelebrationsTab = getByTestId('PartnerCareTabCelebrations'); - expect(CelebrationsTab.textContent).toEqual('Celebrations (0)'); - fireEvent.click(CelebrationsTab); - expect(getByTestId('PartnerCareCelebrationCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('PartnerCarePrayerCardContentEmpty')).not.toBeInTheDocument(); - }); + it('empty', () => { + const prayerRequestTasks = { + nodes: [], + totalCount: 0, + }; + const reportsPeopleWithBirthdays = { + periods: [{ people: [] }], + totalCount: 0, + }; + const reportsPeopleWithAnniversaries = { + periods: [{ people: [] }], + totalCount: 0, + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect( + getByTestId('PartnerCarePrayerCardContentEmpty'), + ).toBeInTheDocument(); + expect( + queryByTestId('PartnerCareCelebrationCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('PartnerCareTabPrayer').textContent).toEqual( + 'Prayer (0)', + ); + const CelebrationsTab = getByTestId('PartnerCareTabCelebrations'); + expect(CelebrationsTab.textContent).toEqual('Celebrations (0)'); + fireEvent.click(CelebrationsTab); + expect( + getByTestId('PartnerCareCelebrationCardContentEmpty'), + ).toBeInTheDocument(); + expect( + queryByTestId('PartnerCarePrayerCardContentEmpty'), + ).not.toBeInTheDocument(); + }); - it('props', () => { - const prayerRequestTasks: GetThisWeekQuery_prayerRequestTasks = { - nodes: [ - { - id: 'task_1', - subject: 'the quick brown fox jumps over the lazy dog', - activityType: ActivityTypeEnum.PRAYER_REQUEST, - contacts: { nodes: [{ name: 'Roger Smith' }, { name: 'Sarah Smith' }] }, - startAt: null, - completedAt: null, - }, - { - id: 'task_2', - subject: 'on the boat to see uncle johnny', - activityType: ActivityTypeEnum.PRAYER_REQUEST, - contacts: { nodes: [{ name: 'Roger Parker' }, { name: 'Sarah Parker' }] }, - startAt: null, - completedAt: null, - }, - ], - totalCount: 2560, - }; - const personWithBirthday = { - birthdayDay: 1, - birthdayMonth: 1, - firstName: 'John', - lastName: 'Doe', - parentContact: { - id: 'contact', - }, - }; - const personWithAnniversary = { - anniversaryDay: 5, - anniversaryMonth: 10, - parentContact: { - id: 'contact', - name: 'John and Sarah, Doe', - }, - }; - const reportsPeopleWithBirthdays = { - periods: [ - { - people: [ - { ...personWithBirthday, id: 'person_1' }, - { ...personWithBirthday, id: 'person_2' }, - ], - }, - ], - }; - const reportsPeopleWithAnniversaries = { - periods: [ - { - people: [ - { ...personWithAnniversary, id: 'person_3' }, - { ...personWithAnniversary, id: 'person_4' }, - ], - }, - ], - }; - const { getByTestId, queryByTestId } = render( - , - ); - expect(queryByTestId('PartnerCarePrayerCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('PartnerCarePrayerList')).toBeInTheDocument(); - expect(getByTestId('PartnerCareTabPrayer').textContent).toEqual('Prayer (2,560)'); - const task1Element = getByTestId('PartnerCarePrayerListItem-task_1'); - expect(task1Element.textContent).toEqual('Roger Smith, Sarah Smiththe quick brown fox jumps over the lazy dog'); - userEvent.click(task1Element); - expect(openTaskDrawer).toHaveBeenCalledWith({ taskId: 'task_1' }); - expect(getByTestId('PartnerCarePrayerListItem-task_2').textContent).toEqual( - 'Roger Parker, Sarah Parkeron the boat to see uncle johnny', - ); - const CelebrationsTab = getByTestId('PartnerCareTabCelebrations'); - expect(CelebrationsTab.textContent).toEqual('Celebrations (4)'); - fireEvent.click(CelebrationsTab); - expect(queryByTestId('PartnerCareCelebrationCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('PartnerCareCelebrationList')).toBeInTheDocument(); - expect(getByTestId('PartnerCareBirthdayListItem-person_1').textContent).toEqual('John DoeJan 1'); - expect(getByTestId('PartnerCareBirthdayListItem-person_2').textContent).toEqual('John DoeJan 1'); - expect(getByTestId('PartnerCareAnniversaryListItem-person_3').textContent).toEqual('John and Sarah, DoeOct 5'); - expect(queryByTestId('PartnerCareAnniversaryListItem-person_4')).not.toBeInTheDocument(); - }); + it('props', () => { + const prayerRequestTasks: GetThisWeekQuery_prayerRequestTasks = { + nodes: [ + { + id: 'task_1', + subject: 'the quick brown fox jumps over the lazy dog', + activityType: ActivityTypeEnum.PRAYER_REQUEST, + contacts: { + nodes: [{ name: 'Roger Smith' }, { name: 'Sarah Smith' }], + }, + startAt: null, + completedAt: null, + }, + { + id: 'task_2', + subject: 'on the boat to see uncle johnny', + activityType: ActivityTypeEnum.PRAYER_REQUEST, + contacts: { + nodes: [{ name: 'Roger Parker' }, { name: 'Sarah Parker' }], + }, + startAt: null, + completedAt: null, + }, + ], + totalCount: 2560, + }; + const personWithBirthday = { + birthdayDay: 1, + birthdayMonth: 1, + firstName: 'John', + lastName: 'Doe', + parentContact: { + id: 'contact', + }, + }; + const personWithAnniversary = { + anniversaryDay: 5, + anniversaryMonth: 10, + parentContact: { + id: 'contact', + name: 'John and Sarah, Doe', + }, + }; + const reportsPeopleWithBirthdays = { + periods: [ + { + people: [ + { ...personWithBirthday, id: 'person_1' }, + { ...personWithBirthday, id: 'person_2' }, + ], + }, + ], + }; + const reportsPeopleWithAnniversaries = { + periods: [ + { + people: [ + { ...personWithAnniversary, id: 'person_3' }, + { ...personWithAnniversary, id: 'person_4' }, + ], + }, + ], + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect( + queryByTestId('PartnerCarePrayerCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('PartnerCarePrayerList')).toBeInTheDocument(); + expect(getByTestId('PartnerCareTabPrayer').textContent).toEqual( + 'Prayer (2,560)', + ); + const task1Element = getByTestId('PartnerCarePrayerListItem-task_1'); + expect(task1Element.textContent).toEqual( + 'Roger Smith, Sarah Smiththe quick brown fox jumps over the lazy dog', + ); + userEvent.click(task1Element); + expect(openTaskDrawer).toHaveBeenCalledWith({ taskId: 'task_1' }); + expect(getByTestId('PartnerCarePrayerListItem-task_2').textContent).toEqual( + 'Roger Parker, Sarah Parkeron the boat to see uncle johnny', + ); + const CelebrationsTab = getByTestId('PartnerCareTabCelebrations'); + expect(CelebrationsTab.textContent).toEqual('Celebrations (4)'); + fireEvent.click(CelebrationsTab); + expect( + queryByTestId('PartnerCareCelebrationCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('PartnerCareCelebrationList')).toBeInTheDocument(); + expect( + getByTestId('PartnerCareBirthdayListItem-person_1').textContent, + ).toEqual('John DoeJan 1'); + expect( + getByTestId('PartnerCareBirthdayListItem-person_2').textContent, + ).toEqual('John DoeJan 1'); + expect( + getByTestId('PartnerCareAnniversaryListItem-person_3').textContent, + ).toEqual('John and Sarah, DoeOct 5'); + expect( + queryByTestId('PartnerCareAnniversaryListItem-person_4'), + ).not.toBeInTheDocument(); + }); }); diff --git a/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.tsx b/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.tsx index e3dde6eaaa..3c5b1a0cef 100644 --- a/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.tsx +++ b/src/components/Dashboard/ThisWeek/PartnerCare/PartnerCare.tsx @@ -1,21 +1,21 @@ import React, { ReactElement, useState } from 'react'; import { - Box, - Typography, - makeStyles, - Theme, - CardHeader, - CardActions, - Button, - List, - ListItem, - ListItemText, - ListItemSecondaryAction, - Checkbox, - Tabs, - Tab, - ListItemIcon, - CardContent, + Box, + Typography, + makeStyles, + Theme, + CardHeader, + CardActions, + Button, + List, + ListItem, + ListItemText, + ListItemSecondaryAction, + Checkbox, + Tabs, + Tab, + ListItemIcon, + CardContent, } from '@material-ui/core'; import CakeIcon from '@material-ui/icons/Cake'; import FavoriteIcon from '@material-ui/icons/Favorite'; @@ -27,10 +27,10 @@ import Link from 'next/link'; import { dayMonthFormat } from '../../../../lib/intlFormat'; import AnimatedCard from '../../../AnimatedCard'; import { - GetThisWeekQuery_prayerRequestTasks, - GetThisWeekQuery_reportsPeopleWithBirthdays, - GetThisWeekQuery_reportsPeopleWithAnniversaries, - GetThisWeekQuery_prayerRequestTasks_nodes as Task, + GetThisWeekQuery_prayerRequestTasks, + GetThisWeekQuery_reportsPeopleWithBirthdays, + GetThisWeekQuery_reportsPeopleWithAnniversaries, + GetThisWeekQuery_prayerRequestTasks_nodes as Task, } from '../../../../../types/GetThisWeekQuery'; import { useApp } from '../../../App'; import TaskStatus from '../../../Task/Status'; @@ -38,323 +38,361 @@ import illustration4 from '../../../../images/drawkit/grape/drawkit-grape-pack-i import illustration7 from '../../../../images/drawkit/grape/drawkit-grape-pack-illustration-7.svg'; const useStyles = makeStyles((theme: Theme) => ({ - div: { - flex: 1, - display: 'flex', - flexDirection: 'column', - overflow: 'auto', + div: { + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'auto', + }, + list: { + flex: 1, + padding: 0, + overflow: 'auto', + }, + card: { + display: 'flex', + flexDirection: 'column', + height: '322px', + [theme.breakpoints.down('xs')]: { + height: 'auto', }, - list: { - flex: 1, - padding: 0, - overflow: 'auto', - }, - card: { - display: 'flex', - flexDirection: 'column', - height: '322px', - [theme.breakpoints.down('xs')]: { - height: 'auto', - }, - }, - cardContent: { - padding: theme.spacing(2), - display: 'flex', - flex: 1, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }, - img: { - height: '120px', - marginBottom: 0, - [theme.breakpoints.down('xs')]: { - height: '150px', - marginBottom: theme.spacing(2), - }, + }, + cardContent: { + padding: theme.spacing(2), + display: 'flex', + flex: 1, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, + img: { + height: '120px', + marginBottom: 0, + [theme.breakpoints.down('xs')]: { + height: '150px', + marginBottom: theme.spacing(2), }, + }, })); interface Props { - accountListId: string; - loading?: boolean; - prayerRequestTasks?: GetThisWeekQuery_prayerRequestTasks; - reportsPeopleWithBirthdays?: GetThisWeekQuery_reportsPeopleWithBirthdays; - reportsPeopleWithAnniversaries?: GetThisWeekQuery_reportsPeopleWithAnniversaries; + accountListId: string; + loading?: boolean; + prayerRequestTasks?: GetThisWeekQuery_prayerRequestTasks; + reportsPeopleWithBirthdays?: GetThisWeekQuery_reportsPeopleWithBirthdays; + reportsPeopleWithAnniversaries?: GetThisWeekQuery_reportsPeopleWithAnniversaries; } const PartnerCare = ({ - accountListId, - loading, - prayerRequestTasks, - reportsPeopleWithBirthdays, - reportsPeopleWithAnniversaries, + accountListId, + loading, + prayerRequestTasks, + reportsPeopleWithBirthdays, + reportsPeopleWithAnniversaries, }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); - const [value, setValue] = useState(0); - const { openTaskDrawer } = useApp(); + const classes = useStyles(); + const { t } = useTranslation(); + const [value, setValue] = useState(0); + const { openTaskDrawer } = useApp(); - const handleClick = ({ id: taskId }: Task): void => { - openTaskDrawer({ taskId }); - }; + const handleClick = ({ id: taskId }: Task): void => { + openTaskDrawer({ taskId }); + }; - const handleChange = (_event: React.ChangeEvent, newValue: number): void => { - setValue(newValue); - }; + const handleChange = (_event: React.ChangeEvent, newValue: number): void => { + setValue(newValue); + }; - return ( - - - - - - - {value == 0 && ( - + + + + + + {value == 0 && ( + + {loading && ( + <> + + {[0, 1].map((index) => ( + + } + secondary={} + /> + + + + + ))} + + + + + + )} + {!loading && ( + <> + {(!prayerRequestTasks || + prayerRequestTasks.nodes.length === 0) && ( + - {loading && ( - <> - - {[0, 1].map((index) => ( - - } - secondary={} - /> - - - - - ))} - - - - - - )} - {!loading && ( - <> - {(!prayerRequestTasks || prayerRequestTasks.nodes.length === 0) && ( - + {t('No prayer requests to show.')} + + )} + {prayerRequestTasks && prayerRequestTasks.nodes.length > 0 && ( + <> + + {prayerRequestTasks.nodes.map((task) => ( + handleClick(task)} + > + + {task.contacts.nodes + .map(({ name }) => name) + .join(', ')} + + } + secondary={ + + + - empty - {t('No prayer requests to show.')} - - )} - {prayerRequestTasks && prayerRequestTasks.nodes.length > 0 && ( - <> - - {prayerRequestTasks.nodes.map((task) => ( - handleClick(task)} - > - - {task.contacts.nodes.map(({ name }) => name).join(', ')} - - } - secondary={ - - - - {task.subject} - - - - } - /> - - - - - ))} - - - - - - - - )} - - )} - - )} - {value == 1 && ( - + + + } + /> + + + + + ))} + + + + + + + + )} + + )} + + )} + {value == 1 && ( + + {loading && ( + + {[0, 1, 2].map((index) => ( + + } + secondary={} + /> + + + + + ))} + + )} + {!loading && ( + <> + {(!reportsPeopleWithBirthdays || + (reportsPeopleWithBirthdays.periods[0].people && + reportsPeopleWithBirthdays.periods[0].people.length === 0)) && + (!reportsPeopleWithAnniversaries || + (reportsPeopleWithAnniversaries.periods[0].people && + reportsPeopleWithAnniversaries.periods[0].people.length === + 0)) ? ( + + empty + {t('No celebrations to show.')} + + ) : ( + - {loading && ( - - {[0, 1, 2].map((index) => ( - - } - secondary={} - /> - - - - - ))} - - )} - {!loading && ( - <> - {(!reportsPeopleWithBirthdays || - (reportsPeopleWithBirthdays.periods[0].people && - reportsPeopleWithBirthdays.periods[0].people.length === 0)) && - (!reportsPeopleWithAnniversaries || - (reportsPeopleWithAnniversaries.periods[0].people && - reportsPeopleWithAnniversaries.periods[0].people.length === 0)) ? ( - ( + + + + + + {person.firstName} {person.lastName} + + } + secondary={ + + + - empty - {t('No celebrations to show.')} - - ) : ( - - {reportsPeopleWithBirthdays.periods[0].people.map((person) => ( - - - - - - {person.firstName} {person.lastName} - - } - secondary={ - - - - {dayMonthFormat( - person.birthdayDay, - person.birthdayMonth - 1, - )} - - - - } - /> - - - - - ))} - {uniqBy( - ({ parentContact: id }) => id, - reportsPeopleWithAnniversaries.periods[0].people, - ).map((person) => ( - - - - - {person.parentContact.name} - } - secondary={ - - - - {dayMonthFormat( - person.anniversaryDay, - person.anniversaryMonth - 1, - )} - - - - } - /> - - - - - ))} - - )} - - )} - - )} - - ); + {dayMonthFormat( + person.birthdayDay, + person.birthdayMonth - 1, + )} + + + + } + /> + + + + + ), + )} + {uniqBy( + ({ parentContact: id }) => id, + reportsPeopleWithAnniversaries.periods[0].people, + ).map((person) => ( + + + + + + {person.parentContact.name} + + } + secondary={ + + + + {dayMonthFormat( + person.anniversaryDay, + person.anniversaryMonth - 1, + )} + + + + } + /> + + + + + ))} + + )} + + )} + + )} + + ); }; export default PartnerCare; diff --git a/src/components/Dashboard/ThisWeek/Referrals/Referrals.stories.tsx b/src/components/Dashboard/ThisWeek/Referrals/Referrals.stories.tsx index aa438da972..411908eb9e 100644 --- a/src/components/Dashboard/ThisWeek/Referrals/Referrals.stories.tsx +++ b/src/components/Dashboard/ThisWeek/Referrals/Referrals.stories.tsx @@ -1,63 +1,71 @@ import React, { ReactElement } from 'react'; import { Box } from '@material-ui/core'; import { - GetThisWeekQuery_recentReferrals, - GetThisWeekQuery_onHandReferrals, + GetThisWeekQuery_recentReferrals, + GetThisWeekQuery_onHandReferrals, } from '../../../../../types/GetThisWeekQuery'; import Referrals from '.'; export default { - title: 'Dashboard/ThisWeek/Referrals', + title: 'Dashboard/ThisWeek/Referrals', }; export const Default = (): ReactElement => { - const contact = { - id: 'contact', - name: 'Smith, Sarah', - }; - const recentReferrals: GetThisWeekQuery_recentReferrals = { - nodes: [ - { ...contact, id: 'contact_1' }, - { ...contact, id: 'contact_2' }, - { ...contact, id: 'contact_3' }, - ], - totalCount: 5, - }; - const onHandReferrals: GetThisWeekQuery_onHandReferrals = { - nodes: [ - { ...contact, id: 'contact_4' }, - { ...contact, id: 'contact_5' }, - { ...contact, id: 'contact_6' }, - ], - totalCount: 5, - }; - return ( - - - - ); + const contact = { + id: 'contact', + name: 'Smith, Sarah', + }; + const recentReferrals: GetThisWeekQuery_recentReferrals = { + nodes: [ + { ...contact, id: 'contact_1' }, + { ...contact, id: 'contact_2' }, + { ...contact, id: 'contact_3' }, + ], + totalCount: 5, + }; + const onHandReferrals: GetThisWeekQuery_onHandReferrals = { + nodes: [ + { ...contact, id: 'contact_4' }, + { ...contact, id: 'contact_5' }, + { ...contact, id: 'contact_6' }, + ], + totalCount: 5, + }; + return ( + + + + ); }; export const Empty = (): ReactElement => { - const recentReferrals: GetThisWeekQuery_recentReferrals = { - nodes: [], - totalCount: 0, - }; - const onHandReferrals: GetThisWeekQuery_onHandReferrals = { - nodes: [], - totalCount: 0, - }; - return ( - - - - ); + const recentReferrals: GetThisWeekQuery_recentReferrals = { + nodes: [], + totalCount: 0, + }; + const onHandReferrals: GetThisWeekQuery_onHandReferrals = { + nodes: [], + totalCount: 0, + }; + return ( + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; diff --git a/src/components/Dashboard/ThisWeek/Referrals/Referrals.test.tsx b/src/components/Dashboard/ThisWeek/Referrals/Referrals.test.tsx index 3df6edec62..6ec558f1c8 100644 --- a/src/components/Dashboard/ThisWeek/Referrals/Referrals.test.tsx +++ b/src/components/Dashboard/ThisWeek/Referrals/Referrals.test.tsx @@ -1,113 +1,157 @@ import React from 'react'; import MockDate from 'mockdate'; -import { render, fireEvent } from '../../../../../__tests__/util/testingLibraryReactMock'; +import { + render, + fireEvent, +} from '../../../../../__tests__/util/testingLibraryReactMock'; import Referrals from '.'; describe('Referrals', () => { - beforeEach(() => { - MockDate.set(new Date('2000-01-01')); - }); + beforeEach(() => { + MockDate.set(new Date('2000-01-01')); + }); - afterEach(() => { - MockDate.reset(); - }); + afterEach(() => { + MockDate.reset(); + }); - it('default', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('ReferralsDivRecent')).toBeInTheDocument(); - expect(queryByTestId('ReferralsDivOnHand')).not.toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecent').textContent).toEqual('Recent (0)'); - const OnHandTab = getByTestId('ReferralsTabOnHand'); - expect(OnHandTab.textContent).toEqual('On Hand (0)'); - fireEvent.click(OnHandTab); - expect(queryByTestId('ReferralsDivRecent')).not.toBeInTheDocument(); - expect(getByTestId('ReferralsDivOnHand')).toBeInTheDocument(); - }); + it('default', () => { + const { getByTestId, queryByTestId } = render(); + expect(getByTestId('ReferralsDivRecent')).toBeInTheDocument(); + expect(queryByTestId('ReferralsDivOnHand')).not.toBeInTheDocument(); + expect(getByTestId('ReferralsTabRecent').textContent).toEqual('Recent (0)'); + const OnHandTab = getByTestId('ReferralsTabOnHand'); + expect(OnHandTab.textContent).toEqual('On Hand (0)'); + fireEvent.click(OnHandTab); + expect(queryByTestId('ReferralsDivRecent')).not.toBeInTheDocument(); + expect(getByTestId('ReferralsDivOnHand')).toBeInTheDocument(); + }); - it('loading', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('ReferralsTabRecentListLoading')).toBeInTheDocument(); - expect(queryByTestId('ReferralsTabOnHandListLoading')).not.toBeInTheDocument(); - fireEvent.click(getByTestId('ReferralsTabOnHand')); - expect(getByTestId('ReferralsTabOnHandListLoading')).toBeInTheDocument(); - expect(queryByTestId('ReferralsTabRecentListLoading')).not.toBeInTheDocument(); - }); + it('loading', () => { + const { getByTestId, queryByTestId } = render(); + expect(getByTestId('ReferralsTabRecentListLoading')).toBeInTheDocument(); + expect( + queryByTestId('ReferralsTabOnHandListLoading'), + ).not.toBeInTheDocument(); + fireEvent.click(getByTestId('ReferralsTabOnHand')); + expect(getByTestId('ReferralsTabOnHandListLoading')).toBeInTheDocument(); + expect( + queryByTestId('ReferralsTabRecentListLoading'), + ).not.toBeInTheDocument(); + }); - it('empty', () => { - const referrals = { - nodes: [], - totalCount: 0, - }; - const { getByTestId, queryByTestId } = render( - , - ); - expect(getByTestId('ReferralsTabRecentCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('ReferralsTabOnHandCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecent').textContent).toEqual('Recent (0)'); - const OnHandTab = getByTestId('ReferralsTabOnHand'); - expect(OnHandTab.textContent).toEqual('On Hand (0)'); - fireEvent.click(OnHandTab); - expect(getByTestId('ReferralsTabOnHandCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('ReferralsTabRecentCardContentEmpty')).not.toBeInTheDocument(); - }); + it('empty', () => { + const referrals = { + nodes: [], + totalCount: 0, + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect( + getByTestId('ReferralsTabRecentCardContentEmpty'), + ).toBeInTheDocument(); + expect( + queryByTestId('ReferralsTabOnHandCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('ReferralsTabRecent').textContent).toEqual('Recent (0)'); + const OnHandTab = getByTestId('ReferralsTabOnHand'); + expect(OnHandTab.textContent).toEqual('On Hand (0)'); + fireEvent.click(OnHandTab); + expect( + getByTestId('ReferralsTabOnHandCardContentEmpty'), + ).toBeInTheDocument(); + expect( + queryByTestId('ReferralsTabRecentCardContentEmpty'), + ).not.toBeInTheDocument(); + }); - it('props', () => { - const recentReferrals = { - nodes: [ - { - id: 'contact_1', - name: 'Smith, Bob', - }, - { - id: 'contact_2', - name: 'Smith, Sarah', - }, - ], - totalCount: 1234, - }; - const onHandReferrals = { - nodes: [ - { - id: 'contact_3', - name: 'Smith, Mike', - }, - { - id: 'contact_4', - name: 'Smith, Shelley', - }, - ], - totalCount: 5678, - }; - const { getByTestId, queryByTestId, getByRole } = render( - , - ); - expect(queryByTestId('ReferralsTabRecentCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecentList')).toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecent').textContent).toEqual('Recent (1,234)'); - const referralElement1 = getByTestId('ReferralsTabRecentListItem-contact_1'); - expect(referralElement1.textContent).toEqual('Smith, Bob'); - expect(referralElement1).toHaveAttribute('href', 'https://stage.mpdx.org/contacts/contact_1'); - const referralElement2 = getByTestId('ReferralsTabRecentListItem-contact_2'); - expect(referralElement2.textContent).toEqual('Smith, Sarah'); - expect(referralElement2).toHaveAttribute('href', 'https://stage.mpdx.org/contacts/contact_2'); - expect(getByRole('link', { name: 'View All (1,234)' })).toHaveAttribute( - 'href', - 'https://stage.mpdx.org/contacts?filters=%7B%22created_at%22%3A%221999-12-18..2000-01-01%22%2C%22referrer%22%3A%22any%22%7D', - ); - const OnHandTab = getByTestId('ReferralsTabOnHand'); - expect(OnHandTab.textContent).toEqual('On Hand (5,678)'); - fireEvent.click(OnHandTab); - expect(queryByTestId('ReferralsTabOnHandCardContentEmpty')).not.toBeInTheDocument(); - expect(getByTestId('ReferralsTabOnHandList')).toBeInTheDocument(); - const referralElement3 = getByTestId('ReferralsTabOnHandListItem-contact_3'); - expect(referralElement3.textContent).toEqual('Smith, Mike'); - expect(referralElement3).toHaveAttribute('href', 'https://stage.mpdx.org/contacts/contact_3'); - const referralElement4 = getByTestId('ReferralsTabOnHandListItem-contact_4'); - expect(referralElement4.textContent).toEqual('Smith, Shelley'); - expect(referralElement4).toHaveAttribute('href', 'https://stage.mpdx.org/contacts/contact_4'); - expect(getByRole('link', { name: 'View All (5,678)' })).toHaveAttribute( - 'href', - 'https://stage.mpdx.org/contacts?filters=%7B%22referrer%22%3A%22any%22%2C%22status%22%3A%22Never%20Contacted%2CAsk%20in%20Future%2CCultivate%20Relationship%2CContact%20for%20Appointment%22%7D', - ); - }); + it('props', () => { + const recentReferrals = { + nodes: [ + { + id: 'contact_1', + name: 'Smith, Bob', + }, + { + id: 'contact_2', + name: 'Smith, Sarah', + }, + ], + totalCount: 1234, + }; + const onHandReferrals = { + nodes: [ + { + id: 'contact_3', + name: 'Smith, Mike', + }, + { + id: 'contact_4', + name: 'Smith, Shelley', + }, + ], + totalCount: 5678, + }; + const { getByTestId, queryByTestId, getByRole } = render( + , + ); + expect( + queryByTestId('ReferralsTabRecentCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('ReferralsTabRecentList')).toBeInTheDocument(); + expect(getByTestId('ReferralsTabRecent').textContent).toEqual( + 'Recent (1,234)', + ); + const referralElement1 = getByTestId( + 'ReferralsTabRecentListItem-contact_1', + ); + expect(referralElement1.textContent).toEqual('Smith, Bob'); + expect(referralElement1).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts/contact_1', + ); + const referralElement2 = getByTestId( + 'ReferralsTabRecentListItem-contact_2', + ); + expect(referralElement2.textContent).toEqual('Smith, Sarah'); + expect(referralElement2).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts/contact_2', + ); + expect(getByRole('link', { name: 'View All (1,234)' })).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts?filters=%7B%22created_at%22%3A%221999-12-18..2000-01-01%22%2C%22referrer%22%3A%22any%22%7D', + ); + const OnHandTab = getByTestId('ReferralsTabOnHand'); + expect(OnHandTab.textContent).toEqual('On Hand (5,678)'); + fireEvent.click(OnHandTab); + expect( + queryByTestId('ReferralsTabOnHandCardContentEmpty'), + ).not.toBeInTheDocument(); + expect(getByTestId('ReferralsTabOnHandList')).toBeInTheDocument(); + const referralElement3 = getByTestId( + 'ReferralsTabOnHandListItem-contact_3', + ); + expect(referralElement3.textContent).toEqual('Smith, Mike'); + expect(referralElement3).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts/contact_3', + ); + const referralElement4 = getByTestId( + 'ReferralsTabOnHandListItem-contact_4', + ); + expect(referralElement4.textContent).toEqual('Smith, Shelley'); + expect(referralElement4).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts/contact_4', + ); + expect(getByRole('link', { name: 'View All (5,678)' })).toHaveAttribute( + 'href', + 'https://stage.mpdx.org/contacts?filters=%7B%22referrer%22%3A%22any%22%2C%22status%22%3A%22Never%20Contacted%2CAsk%20in%20Future%2CCultivate%20Relationship%2CContact%20for%20Appointment%22%7D', + ); + }); }); diff --git a/src/components/Dashboard/ThisWeek/Referrals/Referrals.tsx b/src/components/Dashboard/ThisWeek/Referrals/Referrals.tsx index c0df410117..6ccb644089 100644 --- a/src/components/Dashboard/ThisWeek/Referrals/Referrals.tsx +++ b/src/components/Dashboard/ThisWeek/Referrals/Referrals.tsx @@ -1,18 +1,18 @@ import React, { ReactElement, useState } from 'react'; import { - Typography, - makeStyles, - Theme, - CardHeader, - CardActions, - Button, - List, - ListItem, - ListItemText, - ListItemSecondaryAction, - Tabs, - Tab, - CardContent, + Typography, + makeStyles, + Theme, + CardHeader, + CardActions, + Button, + List, + ListItem, + ListItemText, + ListItemSecondaryAction, + Tabs, + Tab, + CardContent, } from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; import { motion } from 'framer-motion'; @@ -20,204 +20,244 @@ import { useTranslation } from 'react-i18next'; import { endOfDay, formatISO, sub } from 'date-fns'; import AnimatedCard from '../../../AnimatedCard'; import { - GetThisWeekQuery_onHandReferrals, - GetThisWeekQuery_recentReferrals, + GetThisWeekQuery_onHandReferrals, + GetThisWeekQuery_recentReferrals, } from '../../../../../types/GetThisWeekQuery'; import HandoffLink from '../../../HandoffLink'; import illustration4 from '../../../../images/drawkit/grape/drawkit-grape-pack-illustration-4.svg'; const useStyles = makeStyles((theme: Theme) => ({ - div: { - flex: 1, - display: 'flex', - flexDirection: 'column', - overflow: 'auto', + div: { + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'auto', + }, + list: { + flex: 1, + padding: 0, + overflow: 'auto', + }, + card: { + display: 'flex', + flexDirection: 'column', + height: '322px', + [theme.breakpoints.down('xs')]: { + height: 'auto', }, - list: { - flex: 1, - padding: 0, - overflow: 'auto', - }, - card: { - display: 'flex', - flexDirection: 'column', - height: '322px', - [theme.breakpoints.down('xs')]: { - height: 'auto', - }, - }, - cardContent: { - padding: theme.spacing(2), - display: 'flex', - flex: 1, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }, - img: { - height: '120px', - marginBottom: 0, - [theme.breakpoints.down('xs')]: { - height: '150px', - marginBottom: theme.spacing(2), - }, + }, + cardContent: { + padding: theme.spacing(2), + display: 'flex', + flex: 1, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, + img: { + height: '120px', + marginBottom: 0, + [theme.breakpoints.down('xs')]: { + height: '150px', + marginBottom: theme.spacing(2), }, + }, })); interface ReferralsTabProps { - loading: boolean; - referrals: GetThisWeekQuery_onHandReferrals | GetThisWeekQuery_recentReferrals; - tab: 'Recent' | 'OnHand'; + loading: boolean; + referrals: + | GetThisWeekQuery_onHandReferrals + | GetThisWeekQuery_recentReferrals; + tab: 'Recent' | 'OnHand'; } -const ReferralsTab = ({ loading, referrals, tab }: ReferralsTabProps): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); +const ReferralsTab = ({ + loading, + referrals, + tab, +}: ReferralsTabProps): ReactElement => { + const classes = useStyles(); + const { t } = useTranslation(); - return ( + return ( + <> + {loading && ( <> - {loading && ( - <> - - {[0, 1].map((index) => ( - - } - secondary={} - /> - - - - - ))} - - - - - - )} - {!loading && ( - <> - {!referrals || referrals.nodes.length === 0 ? ( - - empty - {t('No referrals to show.')} - - ) : ( - <> - - {referrals.nodes.map((contact) => ( - - - {contact.name}} - /> - - - ))} - - - - - - - - )} - - )} + + {[0, 1].map((index) => ( + + } + secondary={} + /> + + + + + ))} + + + + - ); + )} + {!loading && ( + <> + {!referrals || referrals.nodes.length === 0 ? ( + + empty + {t('No referrals to show.')} + + ) : ( + <> + + {referrals.nodes.map((contact) => ( + + + + {contact.name} + + } + /> + + + ))} + + + + + + + + )} + + )} + + ); }; interface Props { - loading?: boolean; - recentReferrals?: GetThisWeekQuery_recentReferrals; - onHandReferrals?: GetThisWeekQuery_onHandReferrals; + loading?: boolean; + recentReferrals?: GetThisWeekQuery_recentReferrals; + onHandReferrals?: GetThisWeekQuery_onHandReferrals; } -const Referrals = ({ loading, recentReferrals, onHandReferrals }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); - const [value, setValue] = useState(0); +const Referrals = ({ + loading, + recentReferrals, + onHandReferrals, +}: Props): ReactElement => { + const classes = useStyles(); + const { t } = useTranslation(); + const [value, setValue] = useState(0); - const handleChange = (_event: React.ChangeEvent, newValue: number): void => { - setValue(newValue); - }; + const handleChange = (_event: React.ChangeEvent, newValue: number): void => { + setValue(newValue); + }; - return ( - - - - - - - {value == 0 && ( - - - - )} - {value == 1 && ( - - - - )} - - ); + return ( + + + + + + + {value == 0 && ( + + + + )} + {value == 1 && ( + + + + )} + + ); }; export default Referrals; diff --git a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.stories.tsx b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.stories.tsx index 10c1b3fd6a..0abd9db17f 100644 --- a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.stories.tsx +++ b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.stories.tsx @@ -1,61 +1,72 @@ import React, { ReactElement } from 'react'; import { Box } from '@material-ui/core'; import { MockedProvider } from '@apollo/client/testing'; -import { GetThisWeekQuery_dueTasks, GetThisWeekQuery_dueTasks_nodes } from '../../../../../types/GetThisWeekQuery'; +import { + GetThisWeekQuery_dueTasks, + GetThisWeekQuery_dueTasks_nodes, +} from '../../../../../types/GetThisWeekQuery'; import { ActivityTypeEnum } from '../../../../../types/globalTypes'; import TasksDueThisWeek from '.'; export default { - title: 'Dashboard/ThisWeek/TasksDueThisWeek', + title: 'Dashboard/ThisWeek/TasksDueThisWeek', }; export const Default = (): ReactElement => { - const task: GetThisWeekQuery_dueTasks_nodes = { - id: 'task', - subject: 'the quick brown fox jumps over the lazy dog', - activityType: ActivityTypeEnum.PRAYER_REQUEST, - contacts: { nodes: [{ name: 'Smith, Roger' }] }, - startAt: null, - completedAt: null, - }; + const task: GetThisWeekQuery_dueTasks_nodes = { + id: 'task', + subject: 'the quick brown fox jumps over the lazy dog', + activityType: ActivityTypeEnum.PRAYER_REQUEST, + contacts: { nodes: [{ name: 'Smith, Roger' }] }, + startAt: null, + completedAt: null, + }; - const dueTasks: GetThisWeekQuery_dueTasks = { - nodes: [ - { ...task, id: 'task_1' }, - { ...task, id: 'task_2' }, - { ...task, id: 'task_3' }, - ], - totalCount: 5, - }; - return ( - - - - - - ); + const dueTasks: GetThisWeekQuery_dueTasks = { + nodes: [ + { ...task, id: 'task_1' }, + { ...task, id: 'task_2' }, + { ...task, id: 'task_3' }, + ], + totalCount: 5, + }; + return ( + + + + + + ); }; export const Empty = (): ReactElement => { - const dueTasks: GetThisWeekQuery_dueTasks = { - nodes: [], - totalCount: 0, - }; - return ( - - - - - - ); + const dueTasks: GetThisWeekQuery_dueTasks = { + nodes: [], + totalCount: 0, + }; + return ( + + + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - - - ); + return ( + + + + + + ); }; diff --git a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx index b80001d701..7473fb55be 100644 --- a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx +++ b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx @@ -8,93 +8,113 @@ import { useApp } from '../../../App'; import TasksDueThisWeek from '.'; jest.mock('../../../App', () => ({ - useApp: jest.fn(), + useApp: jest.fn(), })); const openTaskDrawer = jest.fn(); beforeEach(() => { - (useApp as jest.Mock).mockReturnValue({ - openTaskDrawer, - }); + (useApp as jest.Mock).mockReturnValue({ + openTaskDrawer, + }); }); describe('TasksDueThisWeek', () => { - it('default', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('TasksDueThisWeekCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekList')).not.toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekListLoading')).not.toBeInTheDocument(); - }); + it('default', () => { + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('TasksDueThisWeekCardContentEmpty')).toBeInTheDocument(); + expect(queryByTestId('TasksDueThisWeekList')).not.toBeInTheDocument(); + expect( + queryByTestId('TasksDueThisWeekListLoading'), + ).not.toBeInTheDocument(); + }); - it('loading', () => { - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('TasksDueThisWeekListLoading')).toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekList')).not.toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekCardContentEmpty')).not.toBeInTheDocument(); - }); + it('loading', () => { + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('TasksDueThisWeekListLoading')).toBeInTheDocument(); + expect(queryByTestId('TasksDueThisWeekList')).not.toBeInTheDocument(); + expect( + queryByTestId('TasksDueThisWeekCardContentEmpty'), + ).not.toBeInTheDocument(); + }); - it('empty', () => { - const dueTasks: GetThisWeekQuery_dueTasks = { - nodes: [], - totalCount: 0, - }; - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('TasksDueThisWeekCardContentEmpty')).toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekList')).not.toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekListLoading')).not.toBeInTheDocument(); - }); + it('empty', () => { + const dueTasks: GetThisWeekQuery_dueTasks = { + nodes: [], + totalCount: 0, + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('TasksDueThisWeekCardContentEmpty')).toBeInTheDocument(); + expect(queryByTestId('TasksDueThisWeekList')).not.toBeInTheDocument(); + expect( + queryByTestId('TasksDueThisWeekListLoading'), + ).not.toBeInTheDocument(); + }); - describe('MockDate', () => { - beforeEach(() => { - MockDate.set(new Date(2020, 1, 1)); - }); + describe('MockDate', () => { + beforeEach(() => { + MockDate.set(new Date(2020, 1, 1)); + }); - afterEach(() => { - MockDate.reset(); - }); + afterEach(() => { + MockDate.reset(); + }); - it('props', () => { - const dueTasks: GetThisWeekQuery_dueTasks = { - nodes: [ - { - id: 'task_1', - subject: 'the quick brown fox jumps over the lazy dog', - activityType: ActivityTypeEnum.PRAYER_REQUEST, - contacts: { nodes: [{ name: 'Smith, Roger' }] }, - startAt: null, - completedAt: null, - }, - { - id: 'task_2', - subject: 'the quick brown fox jumps over the lazy dog', - activityType: ActivityTypeEnum.APPOINTMENT, - contacts: { nodes: [{ name: 'Smith, Sarah' }] }, - startAt: null, - completedAt: null, - }, - ], - totalCount: 1234, - }; - const { getByTestId, queryByTestId } = render(); - expect(getByTestId('TasksDueThisWeekList')).toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekCardContentEmpty')).not.toBeInTheDocument(); - expect(queryByTestId('TasksDueThisWeekListLoading')).not.toBeInTheDocument(); - const viewAllElement = getByTestId('TasksDueThisWeekButtonViewAll'); - expect(viewAllElement).toHaveAttribute( - 'href', - '/accountLists/abc/tasks?completed=false&startAt[max]=2020-02-01', - ); - expect(viewAllElement.textContent).toEqual('View All (1,234)'); - const task1Element = getByTestId('TasksDueThisWeekListItem-task_1'); - expect(task1Element.textContent).toEqual( - 'Smith, RogerPrayer Request — the quick brown fox jumps over the lazy dog', - ); - userEvent.click(task1Element); - expect(openTaskDrawer).toHaveBeenCalledWith({ taskId: 'task_1' }); - expect(getByTestId('TasksDueThisWeekListItem-task_2').textContent).toEqual( - 'Smith, SarahAppointment — the quick brown fox jumps over the lazy dog', - ); - }); + it('props', () => { + const dueTasks: GetThisWeekQuery_dueTasks = { + nodes: [ + { + id: 'task_1', + subject: 'the quick brown fox jumps over the lazy dog', + activityType: ActivityTypeEnum.PRAYER_REQUEST, + contacts: { nodes: [{ name: 'Smith, Roger' }] }, + startAt: null, + completedAt: null, + }, + { + id: 'task_2', + subject: 'the quick brown fox jumps over the lazy dog', + activityType: ActivityTypeEnum.APPOINTMENT, + contacts: { nodes: [{ name: 'Smith, Sarah' }] }, + startAt: null, + completedAt: null, + }, + ], + totalCount: 1234, + }; + const { getByTestId, queryByTestId } = render( + , + ); + expect(getByTestId('TasksDueThisWeekList')).toBeInTheDocument(); + expect( + queryByTestId('TasksDueThisWeekCardContentEmpty'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('TasksDueThisWeekListLoading'), + ).not.toBeInTheDocument(); + const viewAllElement = getByTestId('TasksDueThisWeekButtonViewAll'); + expect(viewAllElement).toHaveAttribute( + 'href', + '/accountLists/abc/tasks?completed=false&startAt[max]=2020-02-01', + ); + expect(viewAllElement.textContent).toEqual('View All (1,234)'); + const task1Element = getByTestId('TasksDueThisWeekListItem-task_1'); + expect(task1Element.textContent).toEqual( + 'Smith, RogerPrayer Request — the quick brown fox jumps over the lazy dog', + ); + userEvent.click(task1Element); + expect(openTaskDrawer).toHaveBeenCalledWith({ taskId: 'task_1' }); + expect( + getByTestId('TasksDueThisWeekListItem-task_2').textContent, + ).toEqual( + 'Smith, SarahAppointment — the quick brown fox jumps over the lazy dog', + ); }); + }); }); diff --git a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.tsx b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.tsx index 89671b45b7..744bc49a0c 100644 --- a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.tsx +++ b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.tsx @@ -1,17 +1,17 @@ import React, { ReactElement } from 'react'; import { - Box, - Typography, - makeStyles, - Theme, - CardHeader, - CardActions, - Button, - List, - ListItem, - ListItemText, - ListItemSecondaryAction, - CardContent, + Box, + Typography, + makeStyles, + Theme, + CardHeader, + CardActions, + Button, + List, + ListItem, + ListItemText, + ListItemSecondaryAction, + CardContent, } from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; import { motion } from 'framer-motion'; @@ -20,177 +20,199 @@ import Link from 'next/link'; import { formatISO } from 'date-fns'; import AnimatedCard from '../../../AnimatedCard'; import { - GetThisWeekQuery_dueTasks, - GetThisWeekQuery_dueTasks_nodes as Task, + GetThisWeekQuery_dueTasks, + GetThisWeekQuery_dueTasks_nodes as Task, } from '../../../../../types/GetThisWeekQuery'; import { useApp } from '../../../App'; import TaskStatus from '../../../Task/Status'; import illustration8 from '../../../../images/drawkit/grape/drawkit-grape-pack-illustration-8.svg'; const useStyles = makeStyles((theme: Theme) => ({ - div: { - flex: 1, - display: 'flex', - flexDirection: 'column', - overflow: 'auto', - }, - list: { - flex: 1, - padding: 0, - overflow: 'auto', - }, - card: { - display: 'flex', - flexDirection: 'column', - height: '322px', - [theme.breakpoints.down('xs')]: { - height: 'auto', - }, - }, - cardContent: { - padding: theme.spacing(2), - display: 'flex', - flex: 1, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - }, - img: { - height: '150px', - marginBottom: theme.spacing(2), + div: { + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'auto', + }, + list: { + flex: 1, + padding: 0, + overflow: 'auto', + }, + card: { + display: 'flex', + flexDirection: 'column', + height: '322px', + [theme.breakpoints.down('xs')]: { + height: 'auto', }, + }, + cardContent: { + padding: theme.spacing(2), + display: 'flex', + flex: 1, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, + img: { + height: '150px', + marginBottom: theme.spacing(2), + }, })); interface Props { - accountListId: string; - loading?: boolean; - dueTasks?: GetThisWeekQuery_dueTasks; + accountListId: string; + loading?: boolean; + dueTasks?: GetThisWeekQuery_dueTasks; } -const TasksDueThisWeek = ({ loading, dueTasks, accountListId }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); - const { openTaskDrawer } = useApp(); +const TasksDueThisWeek = ({ + loading, + dueTasks, + accountListId, +}: Props): ReactElement => { + const classes = useStyles(); + const { t } = useTranslation(); + const { openTaskDrawer } = useApp(); - const handleClick = ({ id: taskId }: Task): void => { - openTaskDrawer({ taskId }); - }; + const handleClick = ({ id: taskId }: Task): void => { + openTaskDrawer({ taskId }); + }; - return ( - - - {loading && ( - - - {[0, 1, 2].map((index) => ( - - } - secondary={} - /> - - - - - ))} - - - - - - )} - {!loading && ( - + + {loading && ( + + + {[0, 1, 2].map((index) => ( + + } + secondary={} + /> + + + + + ))} + + + + + + )} + {!loading && ( + + {!dueTasks || dueTasks.nodes.length === 0 ? ( + + empty + {t('No tasks to show.')} + + ) : ( + <> + + {dueTasks.nodes.map((task) => ( + handleClick(task)} + > + + {task.contacts.nodes + .map(({ name }) => name) + .join(', ')} + + } + secondary={ + + + + { + t( + task.activityType, + ) /* manually added to translation file */ + } + {' '} + + {task.activityType && '—'} {task.subject} + + + + } + /> + + + + + ))} + + + - {!dueTasks || dueTasks.nodes.length === 0 ? ( - - empty - {t('No tasks to show.')} - - ) : ( - <> - - {dueTasks.nodes.map((task) => ( - handleClick(task)} - > - - {task.contacts.nodes.map(({ name }) => name).join(', ')} - - } - secondary={ - - - - { - t( - task.activityType, - ) /* manually added to translation file */ - } - {' '} - - {task.activityType && '—'} {task.subject} - - - - } - /> - - - - - ))} - - - - - - - - )} - - )} - - ); + + + + + )} + + )} + + ); }; export default TasksDueThisWeek; diff --git a/src/components/Dashboard/ThisWeek/ThisWeek.mock.ts b/src/components/Dashboard/ThisWeek/ThisWeek.mock.ts index 0b94803b98..8cdb104c22 100644 --- a/src/components/Dashboard/ThisWeek/ThisWeek.mock.ts +++ b/src/components/Dashboard/ThisWeek/ThisWeek.mock.ts @@ -4,180 +4,188 @@ import { GetThisWeekQuery } from '../../../../types/GetThisWeekQuery'; import { ActivityTypeEnum } from '../../../../types/globalTypes'; import { GET_THIS_WEEK_QUERY } from './ThisWeek'; import { - GetWeeklyActivityQueryDefaultMocks, - GetWeeklyActivityQueryLoadingMocks, - GetWeeklyActivityQueryEmptyMocks, + GetWeeklyActivityQueryDefaultMocks, + GetWeeklyActivityQueryLoadingMocks, + GetWeeklyActivityQueryEmptyMocks, } from './WeeklyActivity/WeeklyActivity.mock'; export const GetThisWeekDefaultMocks = (): MockedResponse[] => { - const task = { - id: 'task', - subject: 'the quick brown fox jumps over the lazy dog', - activityType: ActivityTypeEnum.PRAYER_REQUEST, - contacts: { nodes: [{ name: 'Smith, Roger' }] }, - startAt: new Date(2012, 12, 5, 1, 2), - completedAt: null, - }; - const contact = { - id: 'contact', - name: 'Smith, Sarah', - lateAt: '2012-10-01', - }; - const referral = { - id: 'contact', - name: 'Smith, Sarah', - }; - const personWithBirthday = { - id: 'person', - birthdayDay: 1, - birthdayMonth: 1, - firstName: 'John', - lastName: 'Doe', - parentContact: { - id: 'contact', - }, - }; - const personWithAnniversary = { - id: 'person', - anniversaryDay: 5, - anniversaryMonth: 10, - parentContact: { - id: 'contact', - name: 'John and Sarah, Doe', - }, - }; - const data: GetThisWeekQuery = { - accountList: { - id: 'abc', - primaryAppeal: { - id: 'appeal_1', - name: '2020 End of Year Ask', - amount: 1000, - pledgesAmountTotal: 750, - pledgesAmountProcessed: 500, - amountCurrency: 'EUR', - }, - }, - dueTasks: { - nodes: [ - { ...task, id: 'task_1' }, - { ...task, id: 'task_2' }, - { ...task, id: 'task_3' }, - ], - totalCount: 50, - }, - prayerRequestTasks: { - nodes: [ - { ...task, id: 'task_4' }, - { ...task, id: 'task_5' }, - { ...task, id: 'task_6' }, - ], - totalCount: 80, - }, - latePledgeContacts: { - nodes: [ - { ...contact, id: 'contact_1' }, - { ...contact, id: 'contact_2' }, - { ...contact, id: 'contact_3' }, - ], - totalCount: 5, - }, - reportsPeopleWithBirthdays: { - periods: [ - { - people: [ - { ...personWithBirthday, id: 'person_1' }, - { ...personWithBirthday, id: 'person_2' }, - ], - }, - ], - }, - reportsPeopleWithAnniversaries: { - periods: [ - { - people: [ - { ...personWithAnniversary, id: 'person_3' }, - { ...personWithAnniversary, id: 'person_4' }, - ], - }, - ], - }, - recentReferrals: { - nodes: [ - { ...referral, id: 'contact_4' }, - { ...referral, id: 'contact_5' }, - { ...referral, id: 'contact_6' }, - ], - totalCount: 5, - }, - onHandReferrals: { - nodes: [ - { ...referral, id: 'contact_7' }, - { ...referral, id: 'contact_8' }, - { ...referral, id: 'contact_9' }, - ], - totalCount: 5, + const task = { + id: 'task', + subject: 'the quick brown fox jumps over the lazy dog', + activityType: ActivityTypeEnum.PRAYER_REQUEST, + contacts: { nodes: [{ name: 'Smith, Roger' }] }, + startAt: new Date(2012, 12, 5, 1, 2), + completedAt: null, + }; + const contact = { + id: 'contact', + name: 'Smith, Sarah', + lateAt: '2012-10-01', + }; + const referral = { + id: 'contact', + name: 'Smith, Sarah', + }; + const personWithBirthday = { + id: 'person', + birthdayDay: 1, + birthdayMonth: 1, + firstName: 'John', + lastName: 'Doe', + parentContact: { + id: 'contact', + }, + }; + const personWithAnniversary = { + id: 'person', + anniversaryDay: 5, + anniversaryMonth: 10, + parentContact: { + id: 'contact', + name: 'John and Sarah, Doe', + }, + }; + const data: GetThisWeekQuery = { + accountList: { + id: 'abc', + primaryAppeal: { + id: 'appeal_1', + name: '2020 End of Year Ask', + amount: 1000, + pledgesAmountTotal: 750, + pledgesAmountProcessed: 500, + amountCurrency: 'EUR', + }, + }, + dueTasks: { + nodes: [ + { ...task, id: 'task_1' }, + { ...task, id: 'task_2' }, + { ...task, id: 'task_3' }, + ], + totalCount: 50, + }, + prayerRequestTasks: { + nodes: [ + { ...task, id: 'task_4' }, + { ...task, id: 'task_5' }, + { ...task, id: 'task_6' }, + ], + totalCount: 80, + }, + latePledgeContacts: { + nodes: [ + { ...contact, id: 'contact_1' }, + { ...contact, id: 'contact_2' }, + { ...contact, id: 'contact_3' }, + ], + totalCount: 5, + }, + reportsPeopleWithBirthdays: { + periods: [ + { + people: [ + { ...personWithBirthday, id: 'person_1' }, + { ...personWithBirthday, id: 'person_2' }, + ], }, - }; - return [ + ], + }, + reportsPeopleWithAnniversaries: { + periods: [ { - request: { - query: GET_THIS_WEEK_QUERY, - variables: { - accountListId: 'abc', - endOfDay: formatISO(endOfDay(new Date())), - today: formatISO(endOfDay(new Date()), { representation: 'date' }), - twoWeeksFromNow: formatISO(add(endOfDay(new Date()), { weeks: 2 }), { representation: 'date' }), - twoWeeksAgo: formatISO(sub(endOfDay(new Date()), { weeks: 2 }), { representation: 'date' }), - }, - }, - result: { - data, - }, + people: [ + { ...personWithAnniversary, id: 'person_3' }, + { ...personWithAnniversary, id: 'person_4' }, + ], }, - ...GetWeeklyActivityQueryDefaultMocks(), - ]; + ], + }, + recentReferrals: { + nodes: [ + { ...referral, id: 'contact_4' }, + { ...referral, id: 'contact_5' }, + { ...referral, id: 'contact_6' }, + ], + totalCount: 5, + }, + onHandReferrals: { + nodes: [ + { ...referral, id: 'contact_7' }, + { ...referral, id: 'contact_8' }, + { ...referral, id: 'contact_9' }, + ], + totalCount: 5, + }, + }; + return [ + { + request: { + query: GET_THIS_WEEK_QUERY, + variables: { + accountListId: 'abc', + endOfDay: formatISO(endOfDay(new Date())), + today: formatISO(endOfDay(new Date()), { representation: 'date' }), + twoWeeksFromNow: formatISO(add(endOfDay(new Date()), { weeks: 2 }), { + representation: 'date', + }), + twoWeeksAgo: formatISO(sub(endOfDay(new Date()), { weeks: 2 }), { + representation: 'date', + }), + }, + }, + result: { + data, + }, + }, + ...GetWeeklyActivityQueryDefaultMocks(), + ]; }; export const GetThisWeekEmptyMocks = (): MockedResponse[] => { - const data: GetThisWeekQuery = { - accountList: { - id: 'abc', - primaryAppeal: null, + const data: GetThisWeekQuery = { + accountList: { + id: 'abc', + primaryAppeal: null, + }, + dueTasks: { nodes: [], totalCount: 0 }, + prayerRequestTasks: { nodes: [], totalCount: 0 }, + latePledgeContacts: { nodes: [], totalCount: 0 }, + reportsPeopleWithBirthdays: { periods: [{ people: [] }] }, + reportsPeopleWithAnniversaries: { periods: [{ people: [] }] }, + recentReferrals: { nodes: [], totalCount: 0 }, + onHandReferrals: { nodes: [], totalCount: 0 }, + }; + return [ + { + request: { + query: GET_THIS_WEEK_QUERY, + variables: { + accountListId: 'abc', + endOfDay: formatISO(endOfDay(new Date())), + today: formatISO(endOfDay(new Date()), { representation: 'date' }), + twoWeeksFromNow: formatISO(add(endOfDay(new Date()), { weeks: 2 }), { + representation: 'date', + }), + twoWeeksAgo: formatISO(sub(endOfDay(new Date()), { weeks: 2 }), { + representation: 'date', + }), }, - dueTasks: { nodes: [], totalCount: 0 }, - prayerRequestTasks: { nodes: [], totalCount: 0 }, - latePledgeContacts: { nodes: [], totalCount: 0 }, - reportsPeopleWithBirthdays: { periods: [{ people: [] }] }, - reportsPeopleWithAnniversaries: { periods: [{ people: [] }] }, - recentReferrals: { nodes: [], totalCount: 0 }, - onHandReferrals: { nodes: [], totalCount: 0 }, - }; - return [ - { - request: { - query: GET_THIS_WEEK_QUERY, - variables: { - accountListId: 'abc', - endOfDay: formatISO(endOfDay(new Date())), - today: formatISO(endOfDay(new Date()), { representation: 'date' }), - twoWeeksFromNow: formatISO(add(endOfDay(new Date()), { weeks: 2 }), { representation: 'date' }), - twoWeeksAgo: formatISO(sub(endOfDay(new Date()), { weeks: 2 }), { representation: 'date' }), - }, - }, - result: { - data, - }, - }, - ...GetWeeklyActivityQueryEmptyMocks(), - ]; + }, + result: { + data, + }, + }, + ...GetWeeklyActivityQueryEmptyMocks(), + ]; }; export const GetThisWeekLoadingMocks = (): MockedResponse[] => { - return [ - { - ...GetThisWeekDefaultMocks()[0], - delay: 100931731455, - }, - ...GetWeeklyActivityQueryLoadingMocks(), - ]; + return [ + { + ...GetThisWeekDefaultMocks()[0], + delay: 100931731455, + }, + ...GetWeeklyActivityQueryLoadingMocks(), + ]; }; diff --git a/src/components/Dashboard/ThisWeek/ThisWeek.stories.tsx b/src/components/Dashboard/ThisWeek/ThisWeek.stories.tsx index b4be0feebd..04f4c0fab4 100644 --- a/src/components/Dashboard/ThisWeek/ThisWeek.stories.tsx +++ b/src/components/Dashboard/ThisWeek/ThisWeek.stories.tsx @@ -3,32 +3,41 @@ import { MockedProvider } from '@apollo/client/testing'; import withDispatch from '../../../decorators/withDispatch'; import withMargin from '../../../decorators/withMargin'; import { GetWeeklyActivityQueryLoadingMocks } from './WeeklyActivity/WeeklyActivity.mock'; -import { GetThisWeekEmptyMocks, GetThisWeekDefaultMocks } from './ThisWeek.mock'; +import { + GetThisWeekEmptyMocks, + GetThisWeekDefaultMocks, +} from './ThisWeek.mock'; import ThisWeek from '.'; export default { - title: 'Dashboard/ThisWeek', - decorators: [withDispatch({ type: 'updateAccountListId', accountListId: 'abc' }), withMargin], + title: 'Dashboard/ThisWeek', + decorators: [ + withDispatch({ type: 'updateAccountListId', accountListId: 'abc' }), + withMargin, + ], }; export const Default = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Empty = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - ); + return ( + + + + ); }; diff --git a/src/components/Dashboard/ThisWeek/ThisWeek.test.tsx b/src/components/Dashboard/ThisWeek/ThisWeek.test.tsx index 822d247baf..60b0317919 100644 --- a/src/components/Dashboard/ThisWeek/ThisWeek.test.tsx +++ b/src/components/Dashboard/ThisWeek/ThisWeek.test.tsx @@ -2,53 +2,69 @@ import React from 'react'; import { render, waitFor } from '@testing-library/react'; import { MockedProvider } from '@apollo/client/testing'; import { AppProviderContext } from '../../App/Provider'; -import { GetThisWeekEmptyMocks, GetThisWeekLoadingMocks, GetThisWeekDefaultMocks } from './ThisWeek.mock'; +import { + GetThisWeekEmptyMocks, + GetThisWeekLoadingMocks, + GetThisWeekDefaultMocks, +} from './ThisWeek.mock'; import ThisWeek from '.'; jest.mock('../../App', () => ({ - useApp: (): Partial => ({ - openTaskDrawer: jest.fn(), - }), + useApp: (): Partial => ({ + openTaskDrawer: jest.fn(), + }), })); describe('ThisWeek', () => { - it('default', async () => { - const { getByTestId, queryByTestId } = render( - - - , - ); - await waitFor(() => expect(queryByTestId('PartnerCarePrayerListLoading')).not.toBeInTheDocument()); - expect(getByTestId('PartnerCarePrayerList')).toBeInTheDocument(); - expect(getByTestId('TasksDueThisWeekList')).toBeInTheDocument(); - expect(getByTestId('LateCommitmentsListContacts')).toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecentList')).toBeInTheDocument(); - expect(getByTestId('AppealsBoxName')).toBeInTheDocument(); - }); + it('default', async () => { + const { getByTestId, queryByTestId } = render( + + + , + ); + await waitFor(() => + expect( + queryByTestId('PartnerCarePrayerListLoading'), + ).not.toBeInTheDocument(), + ); + expect(getByTestId('PartnerCarePrayerList')).toBeInTheDocument(); + expect(getByTestId('TasksDueThisWeekList')).toBeInTheDocument(); + expect(getByTestId('LateCommitmentsListContacts')).toBeInTheDocument(); + expect(getByTestId('ReferralsTabRecentList')).toBeInTheDocument(); + expect(getByTestId('AppealsBoxName')).toBeInTheDocument(); + }); - it('loading', () => { - const { getByTestId } = render( - - - , - ); - expect(getByTestId('PartnerCarePrayerListLoading')).toBeInTheDocument(); - expect(getByTestId('TasksDueThisWeekListLoading')).toBeInTheDocument(); - expect(getByTestId('LateCommitmentsDivLoading')).toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecentListLoading')).toBeInTheDocument(); - }); + it('loading', () => { + const { getByTestId } = render( + + + , + ); + expect(getByTestId('PartnerCarePrayerListLoading')).toBeInTheDocument(); + expect(getByTestId('TasksDueThisWeekListLoading')).toBeInTheDocument(); + expect(getByTestId('LateCommitmentsDivLoading')).toBeInTheDocument(); + expect(getByTestId('ReferralsTabRecentListLoading')).toBeInTheDocument(); + }); - it('empty', async () => { - const { getByTestId, queryByTestId } = render( - - - , - ); - await waitFor(() => expect(queryByTestId('PartnerCarePrayerListLoading')).not.toBeInTheDocument()); - expect(getByTestId('PartnerCarePrayerCardContentEmpty')).toBeInTheDocument(); - expect(getByTestId('TasksDueThisWeekCardContentEmpty')).toBeInTheDocument(); - expect(getByTestId('LateCommitmentsCardContentEmpty')).toBeInTheDocument(); - expect(getByTestId('ReferralsTabRecentCardContentEmpty')).toBeInTheDocument(); - expect(getByTestId('AppealsCardContentEmpty')).toBeInTheDocument(); - }); + it('empty', async () => { + const { getByTestId, queryByTestId } = render( + + + , + ); + await waitFor(() => + expect( + queryByTestId('PartnerCarePrayerListLoading'), + ).not.toBeInTheDocument(), + ); + expect( + getByTestId('PartnerCarePrayerCardContentEmpty'), + ).toBeInTheDocument(); + expect(getByTestId('TasksDueThisWeekCardContentEmpty')).toBeInTheDocument(); + expect(getByTestId('LateCommitmentsCardContentEmpty')).toBeInTheDocument(); + expect( + getByTestId('ReferralsTabRecentCardContentEmpty'), + ).toBeInTheDocument(); + expect(getByTestId('AppealsCardContentEmpty')).toBeInTheDocument(); + }); }); diff --git a/src/components/Dashboard/ThisWeek/ThisWeek.tsx b/src/components/Dashboard/ThisWeek/ThisWeek.tsx index ad6d2d2514..193ed27f7d 100644 --- a/src/components/Dashboard/ThisWeek/ThisWeek.tsx +++ b/src/components/Dashboard/ThisWeek/ThisWeek.tsx @@ -13,188 +13,221 @@ import Appeals from './Appeals'; import WeeklyActivity from './WeeklyActivity'; interface Props { - accountListId: string; + accountListId: string; } export const GET_THIS_WEEK_QUERY = gql` - query GetThisWeekQuery( - $accountListId: ID! - $endOfDay: ISO8601DateTime! - $today: ISO8601Date! - $twoWeeksFromNow: ISO8601Date! - $twoWeeksAgo: ISO8601Date! + query GetThisWeekQuery( + $accountListId: ID! + $endOfDay: ISO8601DateTime! + $today: ISO8601Date! + $twoWeeksFromNow: ISO8601Date! + $twoWeeksAgo: ISO8601Date! + ) { + accountList(id: $accountListId) { + id + primaryAppeal { + id + name + amount + pledgesAmountTotal + pledgesAmountProcessed + amountCurrency + } + } + dueTasks: tasks( + accountListId: $accountListId + first: 3 + startAt: { max: $endOfDay } + completed: false ) { - accountList(id: $accountListId) { - id - primaryAppeal { - id - name - amount - pledgesAmountTotal - pledgesAmountProcessed - amountCurrency - } - } - dueTasks: tasks(accountListId: $accountListId, first: 3, startAt: { max: $endOfDay }, completed: false) { - nodes { - id - subject - activityType - startAt - completedAt - contacts { - nodes { - name - } - } - } - totalCount - } - prayerRequestTasks: tasks( - accountListId: $accountListId - first: 3 - activityType: PRAYER_REQUEST - completed: false - ) { - nodes { - id - subject - activityType - startAt - completedAt - contacts { - nodes { - name - } - } - } - totalCount - } - latePledgeContacts: contacts( - accountListId: $accountListId - first: 3 - lateAt: { max: $today } - status: PARTNER_FINANCIAL - ) { - nodes { - id - name - lateAt - } - totalCount + nodes { + id + subject + activityType + startAt + completedAt + contacts { + nodes { + name + } } - reportsPeopleWithBirthdays(accountListId: $accountListId, range: "1m", endDate: $twoWeeksFromNow) { - periods { - people { - id - birthdayDay - birthdayMonth - firstName - lastName - parentContact { - id - } - } - } - } - reportsPeopleWithAnniversaries(accountListId: $accountListId, range: "1m", endDate: $twoWeeksFromNow) { - periods { - people { - id - anniversaryDay - anniversaryMonth - parentContact { - id - name - } - } - } + } + totalCount + } + prayerRequestTasks: tasks( + accountListId: $accountListId + first: 3 + activityType: PRAYER_REQUEST + completed: false + ) { + nodes { + id + subject + activityType + startAt + completedAt + contacts { + nodes { + name + } } - recentReferrals: contacts( - accountListId: $accountListId - first: 3 - referrer: ANY - createdAt: { min: $twoWeeksAgo } - ) { - nodes { - id - name - } - totalCount + } + totalCount + } + latePledgeContacts: contacts( + accountListId: $accountListId + first: 3 + lateAt: { max: $today } + status: PARTNER_FINANCIAL + ) { + nodes { + id + name + lateAt + } + totalCount + } + reportsPeopleWithBirthdays( + accountListId: $accountListId + range: "1m" + endDate: $twoWeeksFromNow + ) { + periods { + people { + id + birthdayDay + birthdayMonth + firstName + lastName + parentContact { + id + } } - onHandReferrals: contacts( - accountListId: $accountListId - first: 3 - status: [NEVER_CONTACTED, ASK_IN_FUTURE, CULTIVATE_RELATIONSHIP, CONTACT_FOR_APPOINTMENT] - referrer: ANY - ) { - nodes { - id - name - } - totalCount + } + } + reportsPeopleWithAnniversaries( + accountListId: $accountListId + range: "1m" + endDate: $twoWeeksFromNow + ) { + periods { + people { + id + anniversaryDay + anniversaryMonth + parentContact { + id + name + } } + } + } + recentReferrals: contacts( + accountListId: $accountListId + first: 3 + referrer: ANY + createdAt: { min: $twoWeeksAgo } + ) { + nodes { + id + name + } + totalCount + } + onHandReferrals: contacts( + accountListId: $accountListId + first: 3 + status: [ + NEVER_CONTACTED + ASK_IN_FUTURE + CULTIVATE_RELATIONSHIP + CONTACT_FOR_APPOINTMENT + ] + referrer: ANY + ) { + nodes { + id + name + } + totalCount } + } `; const ThisWeek = ({ accountListId }: Props): ReactElement => { - const { t } = useTranslation(); - const { data, loading } = useQuery(GET_THIS_WEEK_QUERY, { - variables: { - accountListId, - endOfDay: formatISO(endOfDay(new Date())), - today: formatISO(endOfDay(new Date()), { representation: 'date' }), - twoWeeksFromNow: formatISO(add(endOfDay(new Date()), { weeks: 2 }), { representation: 'date' }), - twoWeeksAgo: formatISO(sub(endOfDay(new Date()), { weeks: 2 }), { representation: 'date' }), - }, - }); + const { t } = useTranslation(); + const { data, loading } = useQuery(GET_THIS_WEEK_QUERY, { + variables: { + accountListId, + endOfDay: formatISO(endOfDay(new Date())), + today: formatISO(endOfDay(new Date()), { representation: 'date' }), + twoWeeksFromNow: formatISO(add(endOfDay(new Date()), { weeks: 2 }), { + representation: 'date', + }), + twoWeeksAgo: formatISO(sub(endOfDay(new Date()), { weeks: 2 }), { + representation: 'date', + }), + }, + }); - const { - dueTasks, - prayerRequestTasks, - latePledgeContacts, - reportsPeopleWithBirthdays, - reportsPeopleWithAnniversaries, - recentReferrals, - onHandReferrals, - accountList, - } = data || {}; + const { + dueTasks, + prayerRequestTasks, + latePledgeContacts, + reportsPeopleWithBirthdays, + reportsPeopleWithAnniversaries, + recentReferrals, + onHandReferrals, + accountList, + } = data || {}; - return ( - <> - - - {t('To Do This Week')} - - - - - - - - - - - - - - - - - - - - - - - - ); + return ( + <> + + + {t('To Do This Week')} + + + + + + + + + + + + + + + + + + + + + + + + ); }; export default ThisWeek; diff --git a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.mock.ts b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.mock.ts index e5eb3858e3..32850d09c6 100644 --- a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.mock.ts +++ b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.mock.ts @@ -4,107 +4,113 @@ import { GetWeeklyActivityQuery } from '../../../../../types/GetWeeklyActivityQu import { GET_WEEKLY_ACTIVITY_QUERY } from './WeeklyActivity'; const data: GetWeeklyActivityQuery = { - completedCalls: { totalCount: 1234 }, - callsThatProducedAppointments: { totalCount: 5678 }, - completedMessages: { totalCount: 9012 }, - messagesThatProducedAppointments: { totalCount: 3456 }, - completedAppointments: { totalCount: 7890 }, - completedCorrespondence: { totalCount: 1234 }, + completedCalls: { totalCount: 1234 }, + callsThatProducedAppointments: { totalCount: 5678 }, + completedMessages: { totalCount: 9012 }, + messagesThatProducedAppointments: { totalCount: 3456 }, + completedAppointments: { totalCount: 7890 }, + completedCorrespondence: { totalCount: 1234 }, }; const dataPreviousWeek: GetWeeklyActivityQuery = { - completedCalls: { totalCount: 5678 }, - callsThatProducedAppointments: { totalCount: 9012 }, - completedMessages: { totalCount: 3456 }, - messagesThatProducedAppointments: { totalCount: 7890 }, - completedAppointments: { totalCount: 1234 }, - completedCorrespondence: { totalCount: 5678 }, + completedCalls: { totalCount: 5678 }, + callsThatProducedAppointments: { totalCount: 9012 }, + completedMessages: { totalCount: 3456 }, + messagesThatProducedAppointments: { totalCount: 7890 }, + completedAppointments: { totalCount: 1234 }, + completedCorrespondence: { totalCount: 5678 }, }; export const GetWeeklyActivityQueryDefaultMocks = ( - startOfWeek = new Date(), - endOfWeek = new Date(), + startOfWeek = new Date(), + endOfWeek = new Date(), ): MockedResponse[] => { - return [ - { - request: { - query: GET_WEEKLY_ACTIVITY_QUERY, - variables: { - accountListId: 'abc', - startOfWeek: moment(startOfWeek).startOf('week').toISOString(), - endOfWeek: moment(endOfWeek).endOf('week').toISOString(), - }, - }, - result: { - data, - }, + return [ + { + request: { + query: GET_WEEKLY_ACTIVITY_QUERY, + variables: { + accountListId: 'abc', + startOfWeek: moment(startOfWeek).startOf('week').toISOString(), + endOfWeek: moment(endOfWeek).endOf('week').toISOString(), }, - { - request: { - query: GET_WEEKLY_ACTIVITY_QUERY, - variables: { - accountListId: 'abc', - startOfWeek: moment(startOfWeek).startOf('week').subtract(1, 'week').toISOString(), - endOfWeek: moment(endOfWeek).endOf('week').subtract(1, 'week').toISOString(), - }, - }, - result: { - data: dataPreviousWeek, - }, + }, + result: { + data, + }, + }, + { + request: { + query: GET_WEEKLY_ACTIVITY_QUERY, + variables: { + accountListId: 'abc', + startOfWeek: moment(startOfWeek) + .startOf('week') + .subtract(1, 'week') + .toISOString(), + endOfWeek: moment(endOfWeek) + .endOf('week') + .subtract(1, 'week') + .toISOString(), }, - { - request: { - query: GET_WEEKLY_ACTIVITY_QUERY, - variables: { - accountListId: 'abc', - startOfWeek: moment(startOfWeek).startOf('week').toISOString(), - endOfWeek: moment(endOfWeek).endOf('week').toISOString(), - }, - }, - result: { - data, - }, + }, + result: { + data: dataPreviousWeek, + }, + }, + { + request: { + query: GET_WEEKLY_ACTIVITY_QUERY, + variables: { + accountListId: 'abc', + startOfWeek: moment(startOfWeek).startOf('week').toISOString(), + endOfWeek: moment(endOfWeek).endOf('week').toISOString(), }, - ]; + }, + result: { + data, + }, + }, + ]; }; const emptyData: GetWeeklyActivityQuery = { - completedCalls: { totalCount: 0 }, - callsThatProducedAppointments: { totalCount: 0 }, - completedMessages: { totalCount: 0 }, - messagesThatProducedAppointments: { totalCount: 0 }, - completedAppointments: { totalCount: 0 }, - completedCorrespondence: { totalCount: 0 }, + completedCalls: { totalCount: 0 }, + callsThatProducedAppointments: { totalCount: 0 }, + completedMessages: { totalCount: 0 }, + messagesThatProducedAppointments: { totalCount: 0 }, + completedAppointments: { totalCount: 0 }, + completedCorrespondence: { totalCount: 0 }, }; export const GetWeeklyActivityQueryEmptyMocks = ( - startOfWeek = new Date(), - endOfWeek = new Date(), + startOfWeek = new Date(), + endOfWeek = new Date(), ): MockedResponse[] => { - return [ - { - request: { - query: GET_WEEKLY_ACTIVITY_QUERY, - variables: { - accountListId: 'abc', - startOfWeek: moment(startOfWeek).startOf('week').toISOString(), - endOfWeek: moment(endOfWeek).endOf('week').toISOString(), - }, - }, - result: { - data: emptyData, - }, + return [ + { + request: { + query: GET_WEEKLY_ACTIVITY_QUERY, + variables: { + accountListId: 'abc', + startOfWeek: moment(startOfWeek).startOf('week').toISOString(), + endOfWeek: moment(endOfWeek).endOf('week').toISOString(), }, - ]; + }, + result: { + data: emptyData, + }, + }, + ]; }; export const GetWeeklyActivityQueryLoadingMocks = ( - startOfWeek = new Date(), - endOfWeek = new Date(), + startOfWeek = new Date(), + endOfWeek = new Date(), ): MockedResponse[] => { - return [ - { - ...GetWeeklyActivityQueryDefaultMocks(startOfWeek, endOfWeek)[0], - delay: 100931731455, - }, - ]; + return [ + { + ...GetWeeklyActivityQueryDefaultMocks(startOfWeek, endOfWeek)[0], + delay: 100931731455, + }, + ]; }; diff --git a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.stories.tsx b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.stories.tsx index b728650988..2d75be2c44 100644 --- a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.stories.tsx +++ b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.stories.tsx @@ -1,29 +1,38 @@ import React, { ReactElement } from 'react'; import { Box } from '@material-ui/core'; import { MockedProvider } from '@apollo/client/testing'; -import { GetWeeklyActivityQueryDefaultMocks, GetWeeklyActivityQueryLoadingMocks } from './WeeklyActivity.mock'; +import { + GetWeeklyActivityQueryDefaultMocks, + GetWeeklyActivityQueryLoadingMocks, +} from './WeeklyActivity.mock'; import WeeklyActivity from '.'; export default { - title: 'Dashboard/ThisWeek/WeeklyActivity', + title: 'Dashboard/ThisWeek/WeeklyActivity', }; export const Default = (): ReactElement => { - return ( - - - - - - ); + return ( + + + + + + ); }; export const Loading = (): ReactElement => { - return ( - - - - - - ); + return ( + + + + + + ); }; diff --git a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.test.tsx b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.test.tsx index 8f5454d77c..c2b35cbe39 100644 --- a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.test.tsx +++ b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.test.tsx @@ -2,71 +2,122 @@ import React from 'react'; import { render, waitFor, fireEvent } from '@testing-library/react'; import { MockedProvider } from '@apollo/client/testing'; import MockDate from 'mockdate'; -import { GetWeeklyActivityQueryDefaultMocks, GetWeeklyActivityQueryLoadingMocks } from './WeeklyActivity.mock'; +import { + GetWeeklyActivityQueryDefaultMocks, + GetWeeklyActivityQueryLoadingMocks, +} from './WeeklyActivity.mock'; import WeeklyActivity from '.'; describe('WeeklyActivity', () => { - it('loading', () => { - const { getByTestId } = render( - - - , - ); - expect(getByTestId('WeeklyActivityTableCellCompletedCalls').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('WeeklyActivityTableCellCallsThatProducedAppointments').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('WeeklyActivityTableCellCompletedMessages').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('WeeklyActivityTableCellMessagesThatProducedAppointments').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('WeeklyActivityTableCellCompletedAppointments').children[0].className).toContain( - 'MuiSkeleton-root', - ); - expect(getByTestId('WeeklyActivityTableCellCompletedCorrespondence').children[0].className).toContain( - 'MuiSkeleton-root', - ); - }); + it('loading', () => { + const { getByTestId } = render( + + + , + ); + expect( + getByTestId('WeeklyActivityTableCellCompletedCalls').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('WeeklyActivityTableCellCallsThatProducedAppointments') + .children[0].className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('WeeklyActivityTableCellCompletedMessages').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('WeeklyActivityTableCellMessagesThatProducedAppointments') + .children[0].className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('WeeklyActivityTableCellCompletedAppointments').children[0] + .className, + ).toContain('MuiSkeleton-root'); + expect( + getByTestId('WeeklyActivityTableCellCompletedCorrespondence').children[0] + .className, + ).toContain('MuiSkeleton-root'); + }); - describe('MockDate', () => { - beforeEach(() => { - MockDate.set(new Date(2020, 1, 1)); - }); + describe('MockDate', () => { + beforeEach(() => { + MockDate.set(new Date(2020, 1, 1)); + }); - afterEach(() => { - MockDate.reset(); - }); + afterEach(() => { + MockDate.reset(); + }); - it('default', async () => { - const { getByTestId, queryByTestId, getByRole } = render( - - - , - ); - expect(getByTestId('WeeklyActivityTableCellDateRange').textContent).toEqual('Jan 26 - Feb 1'); - await waitFor(() => expect(queryByTestId('WeeklyActivitySkeletonLoading')).not.toBeInTheDocument()); - expect(getByTestId('WeeklyActivityTableCellCompletedCalls').textContent).toEqual('1,234'); - expect(getByTestId('WeeklyActivityTableCellCallsThatProducedAppointments').textContent).toEqual('5,678'); - expect(getByTestId('WeeklyActivityTableCellCompletedMessages').textContent).toEqual('9,012'); - expect(getByTestId('WeeklyActivityTableCellMessagesThatProducedAppointments').textContent).toEqual('3,456'); - expect(getByTestId('WeeklyActivityTableCellCompletedAppointments').textContent).toEqual('7,890'); - expect(getByTestId('WeeklyActivityTableCellCompletedCorrespondence').textContent).toEqual('1,234'); - fireEvent.click(getByTestId('WeeklyActivityIconButtonSubtractWeek')); - await waitFor(() => expect(queryByTestId('WeeklyActivitySkeletonLoading')).not.toBeInTheDocument()); - expect(getByTestId('WeeklyActivityTableCellDateRange').textContent).toEqual('Jan 19 - Jan 25'); - expect(getByTestId('WeeklyActivityTableCellCompletedCalls').textContent).toEqual('5,678'); - fireEvent.click(getByTestId('WeeklyActivityIconButtonAddWeek')); - await waitFor(() => expect(queryByTestId('WeeklyActivitySkeletonLoading')).not.toBeInTheDocument()); - expect(getByTestId('WeeklyActivityTableCellDateRange').textContent).toEqual('Jan 26 - Feb 1'); - expect(getByTestId('WeeklyActivityTableCellCompletedCalls').textContent).toEqual('1,234'); - expect(getByRole('link', { name: 'View Activity Detail' })).toHaveAttribute( - 'href', - 'https://stage.mpdx.org/reports/coaching', - ); - }); + it('default', async () => { + const { getByTestId, queryByTestId, getByRole } = render( + + + , + ); + expect( + getByTestId('WeeklyActivityTableCellDateRange').textContent, + ).toEqual('Jan 26 - Feb 1'); + await waitFor(() => + expect( + queryByTestId('WeeklyActivitySkeletonLoading'), + ).not.toBeInTheDocument(), + ); + expect( + getByTestId('WeeklyActivityTableCellCompletedCalls').textContent, + ).toEqual('1,234'); + expect( + getByTestId('WeeklyActivityTableCellCallsThatProducedAppointments') + .textContent, + ).toEqual('5,678'); + expect( + getByTestId('WeeklyActivityTableCellCompletedMessages').textContent, + ).toEqual('9,012'); + expect( + getByTestId('WeeklyActivityTableCellMessagesThatProducedAppointments') + .textContent, + ).toEqual('3,456'); + expect( + getByTestId('WeeklyActivityTableCellCompletedAppointments').textContent, + ).toEqual('7,890'); + expect( + getByTestId('WeeklyActivityTableCellCompletedCorrespondence') + .textContent, + ).toEqual('1,234'); + fireEvent.click(getByTestId('WeeklyActivityIconButtonSubtractWeek')); + await waitFor(() => + expect( + queryByTestId('WeeklyActivitySkeletonLoading'), + ).not.toBeInTheDocument(), + ); + expect( + getByTestId('WeeklyActivityTableCellDateRange').textContent, + ).toEqual('Jan 19 - Jan 25'); + expect( + getByTestId('WeeklyActivityTableCellCompletedCalls').textContent, + ).toEqual('5,678'); + fireEvent.click(getByTestId('WeeklyActivityIconButtonAddWeek')); + await waitFor(() => + expect( + queryByTestId('WeeklyActivitySkeletonLoading'), + ).not.toBeInTheDocument(), + ); + expect( + getByTestId('WeeklyActivityTableCellDateRange').textContent, + ).toEqual('Jan 26 - Feb 1'); + expect( + getByTestId('WeeklyActivityTableCellCompletedCalls').textContent, + ).toEqual('1,234'); + expect( + getByRole('link', { name: 'View Activity Detail' }), + ).toHaveAttribute('href', 'https://stage.mpdx.org/reports/coaching'); }); + }); }); diff --git a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.tsx b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.tsx index c223c2e000..15ede5502c 100644 --- a/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.tsx +++ b/src/components/Dashboard/ThisWeek/WeeklyActivity/WeeklyActivity.tsx @@ -1,17 +1,17 @@ import React, { ReactElement, useState, useEffect } from 'react'; import { - makeStyles, - Theme, - CardHeader, - CardActions, - Button, - IconButton, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - TableContainer, + makeStyles, + Theme, + CardHeader, + CardActions, + Button, + IconButton, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + TableContainer, } from '@material-ui/core'; import { motion } from 'framer-motion'; import { gql, useQuery } from '@apollo/client'; @@ -26,230 +26,282 @@ import { dayMonthFormat, numberFormat } from '../../../../lib/intlFormat'; import HandoffLink from '../../../HandoffLink'; const useStyles = makeStyles((theme: Theme) => ({ - div: { - flex: 1, - display: 'flex', - flexDirection: 'column', - overflow: 'auto', - }, - card: { - display: 'flex', - flexDirection: 'column', - height: '322px', - [theme.breakpoints.down('xs')]: { - height: 'auto', - }, - }, - cardHeader: { - height: '58px', - padding: theme.spacing(0, 2), - }, - cardHeaderAction: { - alignSelf: 'inherit', - marginTop: 0, - }, - tableContainer: { - flex: 1, + div: { + flex: 1, + display: 'flex', + flexDirection: 'column', + overflow: 'auto', + }, + card: { + display: 'flex', + flexDirection: 'column', + height: '322px', + [theme.breakpoints.down('xs')]: { + height: 'auto', }, + }, + cardHeader: { + height: '58px', + padding: theme.spacing(0, 2), + }, + cardHeaderAction: { + alignSelf: 'inherit', + marginTop: 0, + }, + tableContainer: { + flex: 1, + }, })); export const GET_WEEKLY_ACTIVITY_QUERY = gql` - query GetWeeklyActivityQuery($accountListId: ID!, $startOfWeek: ISO8601DateTime!, $endOfWeek: ISO8601DateTime!) { - completedCalls: tasks( - accountListId: $accountListId - completedAt: { min: $startOfWeek, max: $endOfWeek } - activityType: CALL - result: [COMPLETED, DONE] - ) { - totalCount - } - callsThatProducedAppointments: tasks( - accountListId: $accountListId - completedAt: { min: $startOfWeek, max: $endOfWeek } - activityType: CALL - result: [COMPLETED, DONE] - nextAction: APPOINTMENT - ) { - totalCount - } - completedMessages: tasks( - accountListId: $accountListId - completedAt: { min: $startOfWeek, max: $endOfWeek } - activityType: [EMAIL, FACEBOOK_MESSAGE, TEXT_MESSAGE] - result: [COMPLETED, DONE] - ) { - totalCount - } - messagesThatProducedAppointments: tasks( - accountListId: $accountListId - completedAt: { min: $startOfWeek, max: $endOfWeek } - activityType: [EMAIL, FACEBOOK_MESSAGE, TEXT_MESSAGE] - result: [COMPLETED, DONE] - nextAction: APPOINTMENT - ) { - totalCount - } - completedAppointments: tasks( - accountListId: $accountListId - completedAt: { min: $startOfWeek, max: $endOfWeek } - activityType: APPOINTMENT - result: [COMPLETED, DONE] - ) { - totalCount - } - completedCorrespondence: tasks( - accountListId: $accountListId - completedAt: { min: $startOfWeek, max: $endOfWeek } - activityType: [PRE_CALL_LETTER, REMINDER_LETTER, SUPPORT_LETTER, THANK] - result: [COMPLETED, DONE] - ) { - totalCount - } + query GetWeeklyActivityQuery( + $accountListId: ID! + $startOfWeek: ISO8601DateTime! + $endOfWeek: ISO8601DateTime! + ) { + completedCalls: tasks( + accountListId: $accountListId + completedAt: { min: $startOfWeek, max: $endOfWeek } + activityType: CALL + result: [COMPLETED, DONE] + ) { + totalCount + } + callsThatProducedAppointments: tasks( + accountListId: $accountListId + completedAt: { min: $startOfWeek, max: $endOfWeek } + activityType: CALL + result: [COMPLETED, DONE] + nextAction: APPOINTMENT + ) { + totalCount + } + completedMessages: tasks( + accountListId: $accountListId + completedAt: { min: $startOfWeek, max: $endOfWeek } + activityType: [EMAIL, FACEBOOK_MESSAGE, TEXT_MESSAGE] + result: [COMPLETED, DONE] + ) { + totalCount } + messagesThatProducedAppointments: tasks( + accountListId: $accountListId + completedAt: { min: $startOfWeek, max: $endOfWeek } + activityType: [EMAIL, FACEBOOK_MESSAGE, TEXT_MESSAGE] + result: [COMPLETED, DONE] + nextAction: APPOINTMENT + ) { + totalCount + } + completedAppointments: tasks( + accountListId: $accountListId + completedAt: { min: $startOfWeek, max: $endOfWeek } + activityType: APPOINTMENT + result: [COMPLETED, DONE] + ) { + totalCount + } + completedCorrespondence: tasks( + accountListId: $accountListId + completedAt: { min: $startOfWeek, max: $endOfWeek } + activityType: [PRE_CALL_LETTER, REMINDER_LETTER, SUPPORT_LETTER, THANK] + result: [COMPLETED, DONE] + ) { + totalCount + } + } `; interface Props { - accountListId: string; + accountListId: string; } const WeeklyActivity = ({ accountListId }: Props): ReactElement => { - const classes = useStyles(); - const { t } = useTranslation(); + const classes = useStyles(); + const { t } = useTranslation(); - const [startOfWeek, setStartOfWeek] = useState(moment().startOf('week').toISOString()); - const [endOfWeek, setEndOfWeek] = useState(moment().endOf('week').toISOString()); + const [startOfWeek, setStartOfWeek] = useState( + moment().startOf('week').toISOString(), + ); + const [endOfWeek, setEndOfWeek] = useState( + moment().endOf('week').toISOString(), + ); - const { data, loading, refetch } = useQuery(GET_WEEKLY_ACTIVITY_QUERY, { - variables: { - accountListId, - startOfWeek, - endOfWeek, - }, - }); + const { data, loading, refetch } = useQuery( + GET_WEEKLY_ACTIVITY_QUERY, + { + variables: { + accountListId, + startOfWeek, + endOfWeek, + }, + }, + ); - const addWeek = (): void => { - setStartOfWeek((startOfWeek) => moment(startOfWeek).add(1, 'week').toISOString()); - setEndOfWeek((endOfWeek) => moment(endOfWeek).add(1, 'week').toISOString()); - }; + const addWeek = (): void => { + setStartOfWeek((startOfWeek) => + moment(startOfWeek).add(1, 'week').toISOString(), + ); + setEndOfWeek((endOfWeek) => moment(endOfWeek).add(1, 'week').toISOString()); + }; - const subtractWeek = (): void => { - setStartOfWeek((startOfWeek) => moment(startOfWeek).subtract(1, 'week').toISOString()); - setEndOfWeek((endOfWeek) => moment(endOfWeek).subtract(1, 'week').toISOString()); - }; + const subtractWeek = (): void => { + setStartOfWeek((startOfWeek) => + moment(startOfWeek).subtract(1, 'week').toISOString(), + ); + setEndOfWeek((endOfWeek) => + moment(endOfWeek).subtract(1, 'week').toISOString(), + ); + }; - useEffect(() => { - refetch({ - accountListId, - startOfWeek, - endOfWeek, - }); - }, [startOfWeek, endOfWeek]); + useEffect(() => { + refetch({ + accountListId, + startOfWeek, + endOfWeek, + }); + }, [startOfWeek, endOfWeek]); - return ( - - - - - - - - - - } - classes={{ root: classes.cardHeader, action: classes.cardHeaderAction }} - /> - - - - - - - {dayMonthFormat(moment(startOfWeek).date(), moment(startOfWeek).month())} -{' '} - {dayMonthFormat(moment(endOfWeek).date(), moment(endOfWeek).month())} - - {t('Completed')} - {t('Appt Produced')} - - - - - {t('Calls')} - - {loading ? ( - - ) : ( - numberFormat(data.completedCalls.totalCount) - )} - - - {loading ? ( - - ) : ( - numberFormat(data.callsThatProducedAppointments.totalCount) - )} - - - - {t('Messages')} - - {loading ? ( - - ) : ( - numberFormat(data.completedMessages.totalCount) - )} - - - {loading ? ( - - ) : ( - numberFormat(data.messagesThatProducedAppointments.totalCount) - )} - - - - {t('Appointments')} - - {loading ? ( - - ) : ( - numberFormat(data.completedAppointments.totalCount) - )} - - - - - {t('Correspondence')} - - {loading ? ( - - ) : ( - numberFormat(data.completedCorrespondence.totalCount) - )} - - - - -
-
- - - - - -
-
- ); + return ( + + + + + + + + + + } + classes={{ root: classes.cardHeader, action: classes.cardHeaderAction }} + /> + + + + + + + {dayMonthFormat( + moment(startOfWeek).date(), + moment(startOfWeek).month(), + )}{' '} + -{' '} + {dayMonthFormat( + moment(endOfWeek).date(), + moment(endOfWeek).month(), + )} + + {t('Completed')} + {t('Appt Produced')} + + + + + {t('Calls')} + + {loading ? ( + + ) : ( + numberFormat(data.completedCalls.totalCount) + )} + + + {loading ? ( + + ) : ( + numberFormat(data.callsThatProducedAppointments.totalCount) + )} + + + + {t('Messages')} + + {loading ? ( + + ) : ( + numberFormat(data.completedMessages.totalCount) + )} + + + {loading ? ( + + ) : ( + numberFormat( + data.messagesThatProducedAppointments.totalCount, + ) + )} + + + + {t('Appointments')} + + {loading ? ( + + ) : ( + numberFormat(data.completedAppointments.totalCount) + )} + + + + + {t('Correspondence')} + + {loading ? ( + + ) : ( + numberFormat(data.completedCorrespondence.totalCount) + )} + + + + +
+
+ + + + + +
+
+ ); }; export default WeeklyActivity; diff --git a/src/components/Dashboard/Welcome/Welcome.stories.tsx b/src/components/Dashboard/Welcome/Welcome.stories.tsx index f4ae5a6cc9..43da0a9179 100644 --- a/src/components/Dashboard/Welcome/Welcome.stories.tsx +++ b/src/components/Dashboard/Welcome/Welcome.stories.tsx @@ -2,13 +2,13 @@ import React, { ReactElement } from 'react'; import Welcome from '.'; export default { - title: 'Dashboard/Welcome', + title: 'Dashboard/Welcome', }; export const Default = (): ReactElement => { - return ; + return ; }; export const Empty = (): ReactElement => { - return ; + return ; }; diff --git a/src/components/Dashboard/Welcome/Welcome.test.tsx b/src/components/Dashboard/Welcome/Welcome.test.tsx index 65021efb53..4cc8910bef 100644 --- a/src/components/Dashboard/Welcome/Welcome.test.tsx +++ b/src/components/Dashboard/Welcome/Welcome.test.tsx @@ -4,54 +4,66 @@ import { render } from '../../../../__tests__/util/testingLibraryReactMock'; import Welcome from '.'; describe('Welcome', () => { - afterEach(() => { - MockDate.reset(); - }); + afterEach(() => { + MockDate.reset(); + }); - describe('morning', () => { - beforeEach(() => { - MockDate.set(new Date(2000, 1, 1, 0)); - }); + describe('morning', () => { + beforeEach(() => { + MockDate.set(new Date(2000, 1, 1, 0)); + }); - it('default', () => { - const { getByTestId } = render(); - expect(getByTestId('PageHeadingHeading').textContent).toEqual('Good Morning,'); - }); + it('default', () => { + const { getByTestId } = render(); + expect(getByTestId('PageHeadingHeading').textContent).toEqual( + 'Good Morning,', + ); + }); - it('props', () => { - const { getByTestId } = render(); - expect(getByTestId('PageHeadingHeading').textContent).toEqual('Good Morning, John.'); - }); + it('props', () => { + const { getByTestId } = render(); + expect(getByTestId('PageHeadingHeading').textContent).toEqual( + 'Good Morning, John.', + ); + }); + }); + describe('afternoon', () => { + beforeEach(() => { + MockDate.set(new Date(2000, 1, 1, 12)); }); - describe('afternoon', () => { - beforeEach(() => { - MockDate.set(new Date(2000, 1, 1, 12)); - }); - it('default', () => { - const { getByTestId } = render(); - expect(getByTestId('PageHeadingHeading').textContent).toEqual('Good Afternoon,'); - }); + it('default', () => { + const { getByTestId } = render(); + expect(getByTestId('PageHeadingHeading').textContent).toEqual( + 'Good Afternoon,', + ); + }); - it('props', () => { - const { getByTestId } = render(); - expect(getByTestId('PageHeadingHeading').textContent).toEqual('Good Afternoon, John.'); - }); + it('props', () => { + const { getByTestId } = render(); + expect(getByTestId('PageHeadingHeading').textContent).toEqual( + 'Good Afternoon, John.', + ); }); + }); - describe('evening', () => { - beforeEach(() => { - MockDate.set(new Date(2000, 1, 1, 18)); - }); + describe('evening', () => { + beforeEach(() => { + MockDate.set(new Date(2000, 1, 1, 18)); + }); - it('default', () => { - const { getByTestId } = render(); - expect(getByTestId('PageHeadingHeading').textContent).toEqual('Good Evening,'); - }); + it('default', () => { + const { getByTestId } = render(); + expect(getByTestId('PageHeadingHeading').textContent).toEqual( + 'Good Evening,', + ); + }); - it('props', () => { - const { getByTestId } = render(); - expect(getByTestId('PageHeadingHeading').textContent).toEqual('Good Evening, John.'); - }); + it('props', () => { + const { getByTestId } = render(); + expect(getByTestId('PageHeadingHeading').textContent).toEqual( + 'Good Evening, John.', + ); }); + }); }); diff --git a/src/components/Dashboard/Welcome/Welcome.tsx b/src/components/Dashboard/Welcome/Welcome.tsx index 44eec427a0..b2535042d7 100644 --- a/src/components/Dashboard/Welcome/Welcome.tsx +++ b/src/components/Dashboard/Welcome/Welcome.tsx @@ -4,29 +4,35 @@ import PageHeading from '../../PageHeading'; import illustration9 from '../../../images/drawkit/grape/drawkit-grape-pack-illustration-9.svg'; interface Props { - firstName?: string; + firstName?: string; } const Welcome = ({ firstName }: Props): ReactElement => { - const { t } = useTranslation(); - const today = new Date(); - const currentHour = today.getHours(); + const { t } = useTranslation(); + const today = new Date(); + const currentHour = today.getHours(); - let greeting = firstName ? t('Good Evening, {{ firstName }}.', { firstName }) : t('Good Evening,'); + let greeting = firstName + ? t('Good Evening, {{ firstName }}.', { firstName }) + : t('Good Evening,'); - if (currentHour < 12) { - greeting = firstName ? t('Good Morning, {{ firstName }}.', { firstName }) : t('Good Morning,'); - } else if (currentHour < 18) { - greeting = firstName ? t('Good Afternoon, {{ firstName }}.', { firstName }) : t('Good Afternoon,'); - } + if (currentHour < 12) { + greeting = firstName + ? t('Good Morning, {{ firstName }}.', { firstName }) + : t('Good Morning,'); + } else if (currentHour < 18) { + greeting = firstName + ? t('Good Afternoon, {{ firstName }}.', { firstName }) + : t('Good Afternoon,'); + } - return ( - - ); + return ( + + ); }; export default Welcome; diff --git a/src/components/Footer/Footer.stories.tsx b/src/components/Footer/Footer.stories.tsx index de8051cc58..769462ad9d 100644 --- a/src/components/Footer/Footer.stories.tsx +++ b/src/components/Footer/Footer.stories.tsx @@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'; import Footer from '.'; export default { - title: 'Footer', + title: 'Footer', }; export const Default = (): ReactElement =>