From 5e244cc84ea83c76e7d4a1127fdb5d1109fac82a Mon Sep 17 00:00:00 2001 From: Dennis Kigen Date: Mon, 10 Feb 2025 15:05:58 +0300 Subject: [PATCH] (chore) Code quality and tooling improvements (#59) * (chore) Code quality and tooling improvements This PR includes a number of changes to improve code quality and tooling. These changes include: - Adding ESLint plugins for testing (jest-dom, testing-library, playwright) and import rules for preventing duplicate imports. - Adding the react-hooks ESLint plugin for enforcing rules of hooks and exhaustive-deps for React hooks. - Resolving rules-of-hooks violations flagged by the react-hooks ESLint plugin. - Adapting the Prettier configuration to align with similar configurations in O3. - Adding emojis to steps in the GitHub Actions workflows. - Improving the `react-i18next` test mock to properly handle interpolated values in translation strings. - Annotating type-only imports with the `type` keyword. * Review feedback - reorganize imports --- .eslintignore | 2 +- .eslintrc | 29 +- .github/workflows/ci.yml | 105 +++-- .github/workflows/e2e.yml | 5 +- .github/workflows/size.yml | 4 +- .prettierignore | 1 + README.md | 14 +- __mocks__/react-i18next.js | 15 +- e2e/fixtures/api.ts | 2 + jest.config.js | 2 +- package.json | 6 +- .../src/config-schema.ts | 2 - .../src/import/import.component.tsx | 6 +- .../src/import/import.resource.ts | 2 +- .../src/import/import.test.tsx | 3 +- .../import-items.component.tsx | 10 +- .../import-overview.component.tsx | 7 +- .../previous-imports.component.tsx | 4 +- .../src/root.component.tsx | 5 +- .../src/subscription/subscription.test.tsx | 25 +- .../tsconfig.json | 3 +- .../edit-scheduled-report-form.component.tsx | 20 +- .../next-report-execution.component.tsx | 2 +- .../src/components/overlay.component.tsx | 14 +- .../src/components/overview.component.tsx | 167 ++++---- .../report-parameter-input.component.tsx | 31 +- .../components/report-status.component.tsx | 21 +- .../cancel-report-modal.component.tsx | 126 +++--- .../run-report/run-report-form.component.tsx | 34 +- ...eduled-overview-cell-content.component.tsx | 12 +- .../scheduled-overview.component.tsx | 12 +- .../scheduled-report-status.component.tsx | 6 +- .../cron-date-picker.component.tsx | 30 +- .../cron-day-of-month-select.component.tsx | 32 +- .../cron-day-of-week-select.component.tsx | 40 +- .../cron-time-picker.component.tsx | 34 +- .../simple-cron-editor.component.tsx | 214 +++++----- .../hooks/{useOverlay.tsx => useOverlay.ts} | 2 +- packages/esm-reports-app/src/index.ts | 24 -- packages/esm-reports-app/src/reports-link.tsx | 6 +- .../esm-reports-app/src/reports.component.tsx | 4 +- packages/esm-reports-app/translations/en.json | 31 +- packages/esm-reports-app/tsconfig.json | 1 - packages/esm-reports-app/webpack.config.js | 2 +- .../src/dashboard/card.component.tsx | 2 - .../src/root.component.tsx | 1 - packages/esm-system-admin-app/tsconfig.json | 4 +- prettier.config.js | 6 +- yarn.lock | 393 +++++++++++++++++- 49 files changed, 978 insertions(+), 545 deletions(-) rename packages/esm-reports-app/src/hooks/{useOverlay.tsx => useOverlay.ts} (100%) diff --git a/.eslintignore b/.eslintignore index bce97e3..fe31aac 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,2 @@ -src/**/*.spec.tsx **/node_modules/**/* +__mocks__/* diff --git a/.eslintrc b/.eslintrc index e7fb41f..1b78c13 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,11 +2,25 @@ "env": { "node": true }, - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:jest-dom/recommended"], + "overrides": [ + { + "files": ["**/*.test.tsx"], + "extends": ["plugin:testing-library/react"] + }, + { + "files": ["e2e/**/*.spec.ts"], + "extends": ["plugin:playwright/recommended"], + "rules": { + "testing-library/prefer-screen-queries": "off", + "react-hooks/rules-of-hooks": "off", + "react-hooks/exhaustive-deps": "off" + } + } + ], "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint", "react-hooks"], + "plugins": ["@typescript-eslint", "import", "jest-dom", "react-hooks", "testing-library"], "rules": { - // Disabling these rules for now just to keep the diff small. I'll enable them one by one as we go. "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-types": "off", "@typescript-eslint/no-explicit-any": "off", @@ -20,12 +34,12 @@ "fixStyle": "inline-type-imports" } ], - "prefer-const": "off", + "import/no-duplicates": "error", "no-console": ["error", { "allow": ["warn", "error"] }], - "no-unsafe-optional-chaining": "off", "no-explicit-any": "off", "no-extra-boolean-cast": "off", "no-prototype-builtins": "off", + "no-unsafe-optional-chaining": "off", "no-useless-escape": "off", "no-restricted-imports": [ "error", @@ -52,6 +66,9 @@ } ] } - ] + ], + "prefer-const": "off", + "react-hooks/exhaustive-deps": "warn", + "react-hooks/rules-of-hooks": "error" } } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1021c38..f7db9a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,5 @@ name: OpenMRS CI -env: - TURBO_API: 'http://127.0.0.1:9080' - TURBO_TOKEN: ${{ secrets.TURBO_SERVER_TOKEN }} - TURBO_TEAM: ${{ github.repository_owner }} - on: push: branches: [main] @@ -14,42 +9,46 @@ on: types: - created -jobs: +env: + TURBO_API: 'http://127.0.0.1:9080' + TURBO_TOKEN: ${{ secrets.TURBO_SERVER_TOKEN }} + TURBO_TEAM: ${{ github.repository_owner }} +jobs: build: runs-on: ubuntu-latest - timeout-minutes: 15 + steps: - uses: actions/checkout@v4 - - name: Use Node.js + - name: ๐Ÿ› ๏ธ Setup Node.js uses: actions/setup-node@v4 with: - node-version: "18" + node-version: '18' - - name: Cache dependencies + - name: ๐Ÿ’พ Cache dependencies id: cache uses: actions/cache@v4 with: path: '**/node_modules' key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - name: Install dependencies + - name: ๐Ÿ“ฆ Install dependencies if: steps.cache.outputs.cache-hit != 'true' run: yarn install --immutable - - name: Setup a local cache server for Turborepo + - name: ๐Ÿš€ Setup local cache server for Turborepo uses: felixmosh/turborepo-gh-artifacts@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - server-token: ${{ secrets.TURBO_SERVER_TOKEN }} + server-token: ${{ env.TURBO_TOKEN }} - - name: Run lint, typecheck and tests + - name: ๐Ÿงช Run tests, lint and type checks run: yarn verify - - name: Run build - run: yarn turbo run build --color --concurrency=5 + - name: ๐Ÿ—๏ธ Run build + run: yarn turbo run build - - name: Upload build artifacts + - name: ๐Ÿ“ค Upload Artifacts uses: actions/upload-artifact@v4 with: name: packages @@ -60,42 +59,49 @@ jobs: pre_release: runs-on: ubuntu-latest needs: build + if: ${{ github.event_name == 'push' }} steps: - uses: actions/checkout@v4 - - name: Use Node.js + - name: ๐Ÿ› ๏ธ Setup Node.js uses: actions/setup-node@v4 with: - node-version: "18" - registry-url: "https://registry.npmjs.org" + node-version: '18' - - name: Cache dependencies + - name: ๐Ÿ’พ Cache dependencies id: cache uses: actions/cache@v4 with: path: '**/node_modules' key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - name: Install dependencies + - name: ๐Ÿ“ฆ Install dependencies if: steps.cache.outputs.cache-hit != 'true' run: yarn install --immutable - - name: Version + - name: ๐Ÿš€ Setup local cache server for Turborepo + uses: felixmosh/turborepo-gh-artifacts@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + server-token: ${{ env.TURBO_TOKEN }} + + - name: ๐Ÿท๏ธ Version run: yarn workspaces foreach --worktree --topological --exclude @openmrs/esm-admin-tools version "$(node -e "console.log(require('semver').inc(require('./package.json').version, 'patch'))")-pre.${{ github.run_number }}" - - name: Build - run: yarn turbo run build --color --concurrency=5 + - name: ๐Ÿ—๏ธ Build + run: yarn turbo run build - - run: git config user.email "info@openmrs.org" && git config user.name "OpenMRS CI" - - run: git add . && git commit -m "Prerelease version" --no-verify + - name: ๐Ÿ”ง Configure Git + run: git config user.email "info@openmrs.org" && git config user.name "OpenMRS CI" + - name: ๐Ÿ’พ Commit changes + run: git add . && git commit -m "Prerelease version" --no-verify - - name: Pre-release + - name: ๐Ÿš€ Pre-release run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn run ci:prepublish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} - - - name: Upload Artifacts + - name: ๐Ÿ“ค Upload Artifacts uses: actions/upload-artifact@v4 with: name: packages @@ -105,44 +111,63 @@ jobs: release: runs-on: ubuntu-latest + needs: build + if: ${{ github.event_name == 'release' }} steps: - uses: actions/checkout@v4 - - name: Use Node.js + - name: ๐Ÿ“ฅ Download Artifacts + uses: actions/download-artifact@v4 + - name: ๐Ÿ› ๏ธ Use Node.js uses: actions/setup-node@v4 with: - node-version: "18" - registry-url: "https://registry.npmjs.org" + node-version: '18' - - name: Cache dependencies + - name: ๐Ÿ’พ Cache dependencies id: cache uses: actions/cache@v4 with: path: '**/node_modules' key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - name: Install dependencies + - name: ๐Ÿ“ฆ Install dependencies if: steps.cache.outputs.cache-hit != 'true' run: yarn install --immutable - - name: Build - run: yarn turbo run build --color --concurrency=5 + - name: ๐Ÿš€ Setup local cache server for Turborepo + uses: felixmosh/turborepo-gh-artifacts@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + server-token: ${{ env.TURBO_TOKEN }} - - run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn run ci:publish + - name: ๐Ÿ—๏ธ Build + run: yarn turbo run build + + - name: ๐Ÿš€ Publish to NPM + run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn run ci:publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} + - name: ๐Ÿ“ค Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: | + dist + overwrite: true + deploy_admin_tools: runs-on: ubuntu-latest needs: pre_release + if: ${{ github.event_name == 'push' }} steps: - - name: Trigger RefApp Build + - name: ๐Ÿš€ Trigger RefApp Build uses: fjogeleit/http-request-action@v1 with: url: https://ci.openmrs.org/rest/api/latest/queue/O3-BF - method: "POST" + method: 'POST' customHeaders: '{ "Authorization": "Bearer ${{ secrets.BAMBOO_TOKEN }}" }' diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index df4de99..edd4bbf 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -17,7 +17,6 @@ jobs: run-e2e-tests: runs-on: ubuntu-latest timeout-minutes: 15 - steps: - name: ๐Ÿ“ฅ Checkout repo uses: actions/checkout@v4 @@ -53,9 +52,9 @@ jobs: ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }} - - name: ๐ŸŽญ Install Playwright browsers + - name: ๐ŸŽญ Install Playwright Browsers + run: npx playwright install chromium --with-deps if: steps.playwright-cache.outputs.cache-hit != 'true' - run: yarn playwright install --with-deps - name: ๐Ÿš€ Setup local cache server for Turborepo uses: felixmosh/turborepo-gh-artifacts@v3 diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml index 701481d..f0a4c63 100644 --- a/.github/workflows/size.yml +++ b/.github/workflows/size.yml @@ -15,6 +15,6 @@ jobs: - name: ๐Ÿ“ Compute bundle size differences uses: preactjs/compressed-size-action@v2 with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" + repo-token: '${{ secrets.GITHUB_TOKEN }}' minimum-change-threshold: 10000 # 10 KB - build-script: "turbo run build" + build-script: 'turbo run build' diff --git a/.prettierignore b/.prettierignore index 393280d..917cba9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,3 +2,4 @@ dist/ node_modules/ **/*.md **/*.json +.eslintrc diff --git a/README.md b/README.md index a363c7b..c046b43 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ ## What is this? -openmrs-esm-admin-tools provides microfrontends for various administrative functions and administrative modules in the OpenMRS ecosystem. It provides tools to allow admin users to effectively manage their 3.x installation without needing to drop to the v1 console. +Admin Tools provides microfrontends for various administrative functions and administrative modules in the OpenMRS ecosystem. It provides tools to allow admin users to effectively manage their 3.x installation without needing to drop to the v1 console. ## How do I configure this module? - + Please see the [O3 Docs config guide](https://o3-docs.openmrs.org/docs/configure-o3/overview#configuring-individual-frontend-modules) for information about configuring modules. ## Setup @@ -18,7 +18,7 @@ See the guidance in the [Developer Documentation](https://o3-docs.openmrs.org/do This repository uses Yarn. -To start the dev server for a specific package, run +To start the dev server for a specific package, run: ```bash yarn start --sources 'packages/esm--app' @@ -26,7 +26,7 @@ yarn start --sources 'packages/esm--app' This will start a dev server for that package. -To start a dev server running all the packages, run +To start a dev server running all the packages, run: ```bash yarn start-all @@ -86,9 +86,9 @@ To set up environment variables for the project, follow these steps: 1. Create a copy of the .env.example file by running the following command: - ```bash - cp example.env .env - ``` + ```bash + cp example.env .env + ``` 2. Open the newly created .env file in the root of the project. diff --git a/__mocks__/react-i18next.js b/__mocks__/react-i18next.js index ab5ca63..c1173b0 100644 --- a/__mocks__/react-i18next.js +++ b/__mocks__/react-i18next.js @@ -29,16 +29,23 @@ const renderNodes = (reactNodes) => { }); }; -const useMock = [(k) => k, {}]; -useMock.t = (k, o) => (o && o.defaultValue) || (typeof o === 'string' ? o : k); +const useMock = [(key) => key, {}]; +useMock.t = (key, defaultValue, options = {}) => { + let translatedString = defaultValue || key; + Object.entries(options).forEach(([k, v]) => { + translatedString = translatedString.replace(new RegExp(`{{${k}}}`, 'g'), v); + }); + + return translatedString; +}; + useMock.i18n = { language: 'en_US' }; module.exports = { // this mock makes sure any components using the translate HoC receive the t function as a prop - Trans: ({ children }) => renderNodes(children), + Trans: ({ children }) => (Array.isArray(children) ? renderNodes(children) : renderNodes([children])), Translation: ({ children }) => children((k) => k, { i18n: {} }), useTranslation: () => useMock, - // mock if needed I18nextProvider: reactI18next.I18nextProvider, initReactI18next: reactI18next.initReactI18next, diff --git a/e2e/fixtures/api.ts b/e2e/fixtures/api.ts index 79347e4..4d410d6 100644 --- a/e2e/fixtures/api.ts +++ b/e2e/fixtures/api.ts @@ -13,6 +13,7 @@ import { type APIRequestContext, type PlaywrightWorkerArgs, type WorkerFixture } * }); * ``` */ +/* eslint-disable react-hooks/rules-of-hooks */ export const api: WorkerFixture = async ({ playwright }, use) => { const ctx = await playwright.request.newContext({ baseURL: `${process.env.E2E_BASE_URL}/ws/rest/v1/`, @@ -24,3 +25,4 @@ export const api: WorkerFixture = async await use(ctx); }; +/* eslint-enable react-hooks/rules-of-hooks */ diff --git a/jest.config.js b/jest.config.js index c64d673..dafe846 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,6 @@ +/** @type {import('jest').Config} */ const path = require('path'); -/** @type {import('jest').Config} */ module.exports = { clearMocks: true, transform: { diff --git a/package.json b/package.json index 5062c53..23032a4 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,11 @@ "dayjs": "^1.11.10", "dotenv": "^16.4.5", "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest-dom": "^5.5.0", + "eslint-plugin-playwright": "^2.2.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-testing-library": "^7.1.1", "husky": "^9.0.11", "i18next": "^23.10.0", "i18next-parser": "^8.13.0", diff --git a/packages/esm-admin-openconceptlab-app/src/config-schema.ts b/packages/esm-admin-openconceptlab-app/src/config-schema.ts index 7f10afa..96f1002 100644 --- a/packages/esm-admin-openconceptlab-app/src/config-schema.ts +++ b/packages/esm-admin-openconceptlab-app/src/config-schema.ts @@ -1,5 +1,3 @@ -import { Type, validator } from '@openmrs/esm-framework'; - export const configSchema = {}; export type Config = {}; diff --git a/packages/esm-admin-openconceptlab-app/src/import/import.component.tsx b/packages/esm-admin-openconceptlab-app/src/import/import.component.tsx index 03fe83b..b570945 100644 --- a/packages/esm-admin-openconceptlab-app/src/import/import.component.tsx +++ b/packages/esm-admin-openconceptlab-app/src/import/import.component.tsx @@ -122,13 +122,13 @@ const Import: React.FC = () => {
- - + +
- + diff --git a/packages/esm-admin-openconceptlab-app/src/import/import.resource.ts b/packages/esm-admin-openconceptlab-app/src/import/import.resource.ts index 6d5cdb5..8e94af7 100644 --- a/packages/esm-admin-openconceptlab-app/src/import/import.resource.ts +++ b/packages/esm-admin-openconceptlab-app/src/import/import.resource.ts @@ -1,5 +1,5 @@ -import { openmrsFetch } from '@openmrs/esm-framework'; import useSWR from 'swr'; +import { openmrsFetch } from '@openmrs/esm-framework'; import type { Import, Subscription } from '../types'; export function useSubscription() { diff --git a/packages/esm-admin-openconceptlab-app/src/import/import.test.tsx b/packages/esm-admin-openconceptlab-app/src/import/import.test.tsx index d918b2c..719181a 100644 --- a/packages/esm-admin-openconceptlab-app/src/import/import.test.tsx +++ b/packages/esm-admin-openconceptlab-app/src/import/import.test.tsx @@ -54,13 +54,14 @@ describe('Import component', () => { }); it('allows starting an import using the subscription', async () => { + const user = userEvent.setup(); mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [mockSubscription] } }); renderWithSwr(); await waitForLoadingToFinish(); mockStartImportWithSubscription.mockResolvedValue({ status: 201 } as unknown as FetchResponse); - await waitFor(() => userEvent.click(screen.getByText('Import from Subscription'))); + await user.click(screen.getByText('Import from Subscription')); expect(mockStartImportWithSubscription).toHaveBeenCalledWith(new AbortController()); expect(mockStartImportWithSubscription).toHaveBeenCalledTimes(1); diff --git a/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-items.component.tsx b/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-items.component.tsx index 9f8033c..48d71e6 100644 --- a/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-items.component.tsx +++ b/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-items.component.tsx @@ -1,4 +1,5 @@ -import { showNotification, usePagination } from '@openmrs/esm-framework'; +import React, { Fragment, useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { DataTableSkeleton, Link, @@ -11,10 +12,9 @@ import { TableHeader, TableRow, } from '@carbon/react'; -import React, { Fragment, useCallback, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { showNotification, usePagination } from '@openmrs/esm-framework'; +import { type ImportItem } from '../../types'; import { getImportDetails } from './import-items.resource'; -import type { ImportItem } from '../../types'; import styles from './import-items.scss'; interface ImportItemsProps { @@ -25,8 +25,8 @@ const ImportItems: React.FC = ({ importUuid }) => { const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(); const [pageSize, setPageSize] = useState(5); - const [selectedImportItems, setSelectedImportItems] = useState([]); + const { results, currentPage, goTo } = usePagination(selectedImportItems, pageSize); const handleImportDetails = useCallback( diff --git a/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-overview.component.tsx b/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-overview.component.tsx index a3b5c2c..984864c 100644 --- a/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-overview.component.tsx +++ b/packages/esm-admin-openconceptlab-app/src/previous-imports/import-overview/import-overview.component.tsx @@ -1,7 +1,8 @@ -import { formatDatetime } from '@openmrs/esm-framework'; import React from 'react'; +import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; -import type { Import } from '../../types'; +import { formatDatetime } from '@openmrs/esm-framework'; +import { type Import } from '../../types'; import ImportItems from './import-items.component'; import styles from './import-overview.scss'; @@ -30,7 +31,7 @@ const ImportOverview: React.FC = ({ selectedImportObject }) {t('result', 'Result:')}
-
+
{selectedImportObject.upToDateItemsCount} {t('conceptsUpToDate', 'concepts up to date')} diff --git a/packages/esm-admin-openconceptlab-app/src/previous-imports/previous-imports.component.tsx b/packages/esm-admin-openconceptlab-app/src/previous-imports/previous-imports.component.tsx index 4db6432..bad50bf 100644 --- a/packages/esm-admin-openconceptlab-app/src/previous-imports/previous-imports.component.tsx +++ b/packages/esm-admin-openconceptlab-app/src/previous-imports/previous-imports.component.tsx @@ -3,9 +3,9 @@ import { Column, DataTable, DataTableSkeleton, + Grid, Pagination, PaginationSkeleton, - Grid, SkeletonText, Table, TableBody, @@ -19,8 +19,8 @@ import { } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import { formatDatetime, showNotification, usePagination } from '@openmrs/esm-framework'; +import { type Import } from '../types'; import { usePreviousImports } from './previous-imports.resource'; -import type { Import } from '../types'; import ImportOverview from './import-overview/import-overview.component'; import styles from './previous-imports.scss'; diff --git a/packages/esm-admin-openconceptlab-app/src/root.component.tsx b/packages/esm-admin-openconceptlab-app/src/root.component.tsx index e977da2..aa88995 100644 --- a/packages/esm-admin-openconceptlab-app/src/root.component.tsx +++ b/packages/esm-admin-openconceptlab-app/src/root.component.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import classNames from 'classnames'; import { Tab, Tabs, TabList, TabPanels, TabPanel } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import Import from './import/import.component'; @@ -9,10 +10,10 @@ import styles from './root.scss'; const Root: React.FC = () => { const { t } = useTranslation(); return ( -
+

{t('moduleTitle', 'OCL Subscription Module')}

- + {t('subscription', 'Subscription')} {t('import', 'Import')} {t('previousImports', 'Previous Imports')} diff --git a/packages/esm-admin-openconceptlab-app/src/subscription/subscription.test.tsx b/packages/esm-admin-openconceptlab-app/src/subscription/subscription.test.tsx index 38ddadd..4178c5b 100644 --- a/packages/esm-admin-openconceptlab-app/src/subscription/subscription.test.tsx +++ b/packages/esm-admin-openconceptlab-app/src/subscription/subscription.test.tsx @@ -3,8 +3,8 @@ import userEvent from '@testing-library/user-event'; import { screen, waitFor } from '@testing-library/react'; import { type FetchResponse, openmrsFetch, showNotification } from '@openmrs/esm-framework'; import { renderWithSwr } from '../../../../tools/test-helpers'; -import { deleteSubscription, updateSubscription } from './subscription.resource'; import { mockSubscription } from '../../../../__mocks__/openconceptlab.mock'; +import { deleteSubscription, updateSubscription } from './subscription.resource'; import Subscription from './subscription.component'; const mockOpenmrsFetch = openmrsFetch as jest.Mock; @@ -49,6 +49,7 @@ describe('Subscription component', () => { }); xit('allows adding a new subscription', async () => { + const user = userEvent.setup(); mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [] } }); renderWithSwr(); await waitForLoadingToFinish(); @@ -59,9 +60,9 @@ describe('Subscription component', () => { mockUpdateSubscription.mockResolvedValueOnce({ status: 201, ok: true } as unknown as FetchResponse); - await waitFor(() => userEvent.type(urlInputField, mockSubscription.url)); - await waitFor(() => userEvent.type(tokenInputField, mockSubscription.token)); - await waitFor(() => userEvent.click(saveButton)); + await user.type(urlInputField, mockSubscription.url); + await user.type(tokenInputField, mockSubscription.token); + await user.click(saveButton); expect(mockUpdateSubscription).toHaveBeenCalledWith( expect.objectContaining(mockSubscription), @@ -79,6 +80,7 @@ describe('Subscription component', () => { }); xit('allows changing the saved subscription', async () => { + const user = userEvent.setup(); mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [mockSubscription] } }); renderWithSwr(); await waitForLoadingToFinish(); @@ -90,13 +92,11 @@ describe('Subscription component', () => { mockUpdateSubscription.mockResolvedValueOnce({ status: 200, ok: true } as unknown as FetchResponse); - await waitFor(() => userEvent.clear(urlInputField)); - await waitFor(() => userEvent.clear(tokenInputField)); - await waitFor(() => - userEvent.type(urlInputField, 'https://api.openconceptlab.org/orgs/openmrs/collections/DemoQueueConcepts/2'), - ); - await waitFor(() => userEvent.type(tokenInputField, 'token123')); - await waitFor(() => userEvent.click(saveButton)); + await user.clear(urlInputField); + await user.clear(tokenInputField); + await user.type(urlInputField, 'https://api.openconceptlab.org/orgs/openmrs/collections/DemoQueueConcepts/2'); + await user.type(tokenInputField, 'token123'); + await user.click(saveButton); expect(mockUpdateSubscription).toHaveBeenCalledWith( expect.objectContaining({ @@ -118,6 +118,7 @@ describe('Subscription component', () => { }); xit('allows removing the saved subscription', async () => { + const user = userEvent.setup(); mockOpenmrsFetch.mockReturnValueOnce({ data: { results: [mockSubscription] } }); renderWithSwr(); await waitForLoadingToFinish(); @@ -127,7 +128,7 @@ describe('Subscription component', () => { mockDeleteSubscription.mockResolvedValueOnce({ status: 204 } as unknown as FetchResponse); - await waitFor(() => userEvent.click(unsubscribeButton)); + await user.click(unsubscribeButton); expect(mockDeleteSubscription).toHaveBeenCalledWith( expect.objectContaining(mockSubscription), diff --git a/packages/esm-admin-openconceptlab-app/tsconfig.json b/packages/esm-admin-openconceptlab-app/tsconfig.json index 54ce28c..29fd672 100644 --- a/packages/esm-admin-openconceptlab-app/tsconfig.json +++ b/packages/esm-admin-openconceptlab-app/tsconfig.json @@ -1,5 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.tsx"] + "include": ["src/**/*", "../../tools/setup-tests.ts"], } diff --git a/packages/esm-reports-app/src/components/edit-scheduled-report/edit-scheduled-report-form.component.tsx b/packages/esm-reports-app/src/components/edit-scheduled-report/edit-scheduled-report-form.component.tsx index 907d8a9..486107b 100644 --- a/packages/esm-reports-app/src/components/edit-scheduled-report/edit-scheduled-report-form.component.tsx +++ b/packages/esm-reports-app/src/components/edit-scheduled-report/edit-scheduled-report-form.component.tsx @@ -1,13 +1,13 @@ import React, { useCallback, useEffect, useState } from 'react'; +import classNames from 'classnames'; import { take } from 'rxjs/operators'; -import styles from './edit-scheduled-report-form.scss'; -import SimpleCronEditor from '../simple-cron-editor/simple-cron-editor.component'; +import { useTranslation } from 'react-i18next'; +import { Button, ButtonSet, Form, Select, SelectItem, Stack } from '@carbon/react'; +import { getCoreTranslation, showSnackbar, useLayoutType } from '@openmrs/esm-framework'; import { useReportDefinition, useReportDesigns, useReportRequest, runReportObservable } from '../reports.resource'; import ReportParameterInput from '../report-parameter-input.component'; -import { Button, ButtonSet, Form, Select, SelectItem, Stack } from '@carbon/react'; -import { useTranslation } from 'react-i18next'; -import { showSnackbar, useLayoutType } from '@openmrs/esm-framework'; -import classNames from 'classnames'; +import SimpleCronEditor from '../simple-cron-editor/simple-cron-editor.component'; +import styles from './edit-scheduled-report-form.scss'; interface EditScheduledReportForm { reportDefinitionUuid: string; @@ -84,7 +84,7 @@ const EditScheduledReportForm: React.FC = ({ }, ); }, - [closePanel, renderModeUuid, reportRequestUuid, reportParameters, schedule], + [reportRequestUuid, reportDefinitionUuid, reportParameters, renderModeUuid, schedule, t, closePanel], ); const handleOnChange = () => { @@ -107,13 +107,13 @@ const EditScheduledReportForm: React.FC = ({ { setReportParameters((state) => ({ ...state, [parameter.name]: parameterValue, })); }} + value={reportRequest?.parameterMappings[parameter.name]} /> ))}
@@ -135,10 +135,10 @@ const EditScheduledReportForm: React.FC = ({
diff --git a/packages/esm-reports-app/src/components/next-report-execution.component.tsx b/packages/esm-reports-app/src/components/next-report-execution.component.tsx index 7ea5be3..e73b4cf 100644 --- a/packages/esm-reports-app/src/components/next-report-execution.component.tsx +++ b/packages/esm-reports-app/src/components/next-report-execution.component.tsx @@ -22,7 +22,7 @@ const NextReportExecution: React.FC = ({ schedule, cur startAt: currentDate.toISOString(), matchCount: 1, }); - return nextExecutions.length == 1 ? dayjs.utc(nextExecutions[0].toString()).format('YYYY-MM-DD HH:mm') : ''; + return nextExecutions.length === 1 ? dayjs.utc(nextExecutions[0].toString()).format('YYYY-MM-DD HH:mm') : ''; })(); return {nextReportExecutionDate}; diff --git a/packages/esm-reports-app/src/components/overlay.component.tsx b/packages/esm-reports-app/src/components/overlay.component.tsx index 0e0724a..ef7360f 100644 --- a/packages/esm-reports-app/src/components/overlay.component.tsx +++ b/packages/esm-reports-app/src/components/overlay.component.tsx @@ -1,12 +1,10 @@ import React from 'react'; -import { Header } from '@carbon/react'; +import classNames from 'classnames'; +import { Header, IconButton } from '@carbon/react'; import { ArrowLeft, Close } from '@carbon/react/icons'; -import { useLayoutType } from '@openmrs/esm-framework'; +import { getCoreTranslation, useLayoutType } from '@openmrs/esm-framework'; import { closeOverlay, useOverlay } from '../hooks/useOverlay'; import styles from './overlay.scss'; -import { IconButton } from '@carbon/react'; -import { t } from 'i18next'; -import classNames from 'classnames'; const Overlay: React.FC = () => { const { header, component, isOverlayOpen } = useOverlay(); @@ -22,7 +20,7 @@ const Overlay: React.FC = () => { })} > {layout === 'tablet' && ( -
closeOverlay()} aria-label="Tablet overlay" className={styles.tabletOverlayHeader}> +
@@ -35,9 +33,9 @@ const Overlay: React.FC = () => {
{header}
closeOverlay()} kind="ghost" - label={t('close', 'Close')} + label={getCoreTranslation('close')} + onClick={closeOverlay} > diff --git a/packages/esm-reports-app/src/components/overview.component.tsx b/packages/esm-reports-app/src/components/overview.component.tsx index b08d0c8..d437537 100644 --- a/packages/esm-reports-app/src/components/overview.component.tsx +++ b/packages/esm-reports-app/src/components/overview.component.tsx @@ -1,3 +1,6 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import classNames from 'classnames'; +import { useTranslation } from 'react-i18next'; import { Button, Checkbox, @@ -11,8 +14,7 @@ import { TableHeader, TableRow, } from '@carbon/react'; -import React, { useCallback, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; +import { Calendar, Download, Play, Save, TrashCan } from '@carbon/react/icons'; import { downloadMultipleReports, downloadReport, preserveReport, useReports } from './reports.resource'; import { ExtensionSlot, @@ -24,17 +26,15 @@ import { userHasAccess, useSession, } from '@openmrs/esm-framework'; -import { Calendar, Download, Play, Save, TrashCan } from '@carbon/react/icons'; -import styles from './reports.scss'; -import { DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES } from './pagination-constants'; import { closeOverlay, launchOverlay } from '../hooks/useOverlay'; -import RunReportForm from './run-report/run-report-form.component'; import Overlay from './overlay.component'; +import ReportOverviewButton from './report-overview-button.component'; import ReportStatus from './report-status.component'; +import RunReportForm from './run-report/run-report-form.component'; import { COMPLETED, RAN_REPORT_STATUSES, SAVED } from './report-statuses-constants'; -import ReportOverviewButton from './report-overview-button.component'; +import { DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES } from './pagination-constants'; import { PRIVILEGE_SYSTEM_DEVELOPER } from '../constants'; -import classNames from 'classnames'; +import styles from './reports.scss'; const OverviewComponent: React.FC = () => { const { t } = useTranslation(); @@ -48,7 +48,7 @@ const OverviewComponent: React.FC = () => { }, [checkedReportUuidsArray]); const tableHeaders = [ - { key: 'reportName', header: t('reportName', 'Report Name') }, + { key: 'reportName', header: t('reportName', 'Report name') }, { key: 'status', header: t('status', 'Status') }, { key: 'requestedBy', header: t('requestedBy', 'Requested by') }, { key: 'requestedOn', header: t('requestedOn', 'Requested on') }, @@ -125,24 +125,27 @@ const OverviewComponent: React.FC = () => { }); } - const handlePreserveReport = useCallback(async (reportRequestUuid: string) => { - preserveReport(reportRequestUuid) - .then(() => { - mutateReports(); - showSnackbar({ - kind: 'success', - title: t('preserveReport', 'Preserve report'), - subtitle: t('reportPreservedSuccessfully', 'Report preserved successfully'), + const handlePreserveReport = useCallback( + async (reportRequestUuid: string) => { + preserveReport(reportRequestUuid) + .then(() => { + mutateReports(); + showSnackbar({ + kind: 'success', + title: t('preserveReport', 'Preserve report'), + subtitle: t('reportPreservedSuccessfully', 'Report preserved successfully'), + }); + }) + .catch(() => { + showSnackbar({ + kind: 'error', + title: t('preserveReport', 'Preserve report'), + subtitle: t('reportPreservingErrorMsg', 'Error during report preserving'), + }); }); - }) - .catch(() => { - showSnackbar({ - kind: 'error', - title: t('preserveReport', 'Preserve report'), - subtitle: t('reportPreservingErrorMsg', 'Error during report preserving'), - }); - }); - }, []); + }, + [mutateReports, t], + ); const launchDeleteReportDialog = (reportRequestUuid: string) => { const dispose = showModal('cancel-report-modal', { @@ -155,43 +158,49 @@ const OverviewComponent: React.FC = () => { }); }; - const handleDownloadReport = useCallback(async (reportRequestUuid: string) => { - try { - const response = await downloadReport(reportRequestUuid); - processAndDownloadFile(response); - clearReportCheckboxes(); - showSnackbar({ - kind: 'success', - title: t('downloadReport', 'Download report'), - subtitle: t('reportDownloadedSuccessfully', 'Report downloaded successfully'), - }); - } catch (error) { - showSnackbar({ - kind: 'error', - title: t('downloadReport', 'Download report'), - subtitle: t('reportDownloadError', 'Error downloading report'), - }); - } - }, []); + const handleDownloadReport = useCallback( + async (reportRequestUuid: string) => { + try { + const response = await downloadReport(reportRequestUuid); + processAndDownloadFile(response); + clearReportCheckboxes(); + showSnackbar({ + kind: 'success', + title: t('reportDownloaded', 'Report downloaded'), + subtitle: t('reportDownloadedSuccessfully', 'Report downloaded successfully'), + }); + } catch (error) { + showSnackbar({ + kind: 'error', + title: t('errorDownloadingReport', 'Error downloading report'), + subtitle: error?.message, + }); + } + }, + [t], + ); - const handleDownloadMultipleReports = useCallback(async (reportRequestUuids) => { - try { - const response = await downloadMultipleReports(reportRequestUuids); - response.forEach((file) => processAndDownloadFile(file)); - clearReportCheckboxes(); - showSnackbar({ - kind: 'success', - title: t('downloadReport', 'Download report(s)'), - subtitle: t('reportDownloadedSuccessfully', 'Report(s) downloaded successfully'), - }); - } catch (error) { - showSnackbar({ - kind: 'error', - title: t('downloadReport', 'Download report(s)s'), - subtitle: t('reportDownloadingErrorMsg', 'Error during report(s) downloading'), - }); - } - }, []); + const handleDownloadMultipleReports = useCallback( + async (reportRequestUuids) => { + try { + const response = await downloadMultipleReports(reportRequestUuids); + response.forEach((file) => processAndDownloadFile(file)); + clearReportCheckboxes(); + showSnackbar({ + kind: 'success', + title: t('reportsDownloaded', 'Reports downloaded'), + subtitle: t('reportsDownloadedSuccessfully', 'Reports downloaded successfully'), + }); + } catch (error) { + showSnackbar({ + kind: 'error', + title: t('errorDownloadingReports', 'Error downloading reports'), + subtitle: error?.message, + }); + } + }, + [t], + ); const processAndDownloadFile = (file) => { const decodedData = window.atob(file.fileContent); @@ -253,11 +262,11 @@ const OverviewComponent: React.FC = () => { @@ -290,25 +299,25 @@ const OverviewComponent: React.FC = () => { {cell.info.header === 'actions' ? (
} - reportRequestUuid={row.id} + label={t('download', 'Download')} onClick={() => handleDownloadReport(row.id)} + reportRequestUuid={row.id} + shouldBeDisplayed={getReportStatus(row) === COMPLETED || getReportStatus(row) === SAVED} /> } - reportRequestUuid={row.id} + label={t('preserve', 'Preserve')} onClick={() => handlePreserveReport(row.id)} + reportRequestUuid={row.id} + shouldBeDisplayed={getReportStatus(row) === COMPLETED && isEligibleReportUser(row.id)} /> } - reportRequestUuid={row.id} + label={t('delete', 'Delete')} onClick={() => launchDeleteReportDialog(row.id)} + reportRequestUuid={row.id} + shouldBeDisplayed={isEligibleReportUser(row.id)} />
) : cell.info.header === 'status' ? ( @@ -333,11 +342,6 @@ const OverviewComponent: React.FC = () => { { if (newPageSize !== pageSize) { setPageSize(newPageSize); @@ -347,6 +351,11 @@ const OverviewComponent: React.FC = () => { setCurrentPage(newPage); } }} + page={currentPage} + pageSize={pageSize} + pageSizes={DEFAULT_PAGE_SIZES} + size={isDesktop(layout) ? 'sm' : 'lg'} + totalItems={reportsTotalCount} /> ) : null}
diff --git a/packages/esm-reports-app/src/components/report-parameter-input.component.tsx b/packages/esm-reports-app/src/components/report-parameter-input.component.tsx index 1442b9f..c5af41f 100644 --- a/packages/esm-reports-app/src/components/report-parameter-input.component.tsx +++ b/packages/esm-reports-app/src/components/report-parameter-input.component.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from 'react'; -import styles from './run-report/run-report-form.scss'; +import React, { useCallback, useEffect, useState } from 'react'; +import { isEqual } from 'lodash-es'; import { DatePicker, DatePickerInput, Select, SelectItem, TextInput } from '@carbon/react'; import { useLocations } from './reports.resource'; -import { isEqual } from 'lodash-es'; +import styles from './run-report/run-report-form.scss'; interface ReportParameterInputProps { parameter: any; @@ -24,20 +24,23 @@ const ReportParameterInput: React.FC = ({ parameter, const { locations } = useLocations(); const [valueInternal, setValueInternal] = useState(getInitialValue(parameter, value)); + const isValueEqual = useCallback( + (valueA, valueB) => { + if (parameter.type === 'java.util.Date') { + return isEqual(new Date(valueA), new Date(valueB)); + } else { + return isEqual(valueA, valueB); + } + }, + [parameter.type], + ); + useEffect(() => { const newInternalValue = getInitialValue(parameter, value); if (!isValueEqual(newInternalValue, valueInternal)) { setValueInternal(newInternalValue); } - }, [value]); - - const isValueEqual = (valueA, valueB) => { - if (parameter.type === 'java.util.Date') { - return isEqual(new Date(valueA), new Date(valueB)); - } else { - return isEqual(valueA, valueB); - } - }; + }, [isValueEqual, parameter, value, valueInternal]); useEffect(() => { if (parameter.type === 'java.util.Date') { @@ -45,7 +48,7 @@ const ReportParameterInput: React.FC = ({ parameter, } else { onChange(valueInternal); } - }, [valueInternal]); + }, [onChange, parameter.type, valueInternal]); const renderParameterElementBasedOnType = () => { switch (parameter.type) { @@ -102,7 +105,7 @@ const ReportParameterInput: React.FC = ({ parameter, function handleOnChange(event) { let eventValue = null; - if (event.target.type == 'checkbox') { + if (event.target.type === 'checkbox') { eventValue = event.target.checked; } else { eventValue = event.target.value; diff --git a/packages/esm-reports-app/src/components/report-status.component.tsx b/packages/esm-reports-app/src/components/report-status.component.tsx index d4e9906..10c4d1b 100644 --- a/packages/esm-reports-app/src/components/report-status.component.tsx +++ b/packages/esm-reports-app/src/components/report-status.component.tsx @@ -1,17 +1,18 @@ import React from 'react'; +import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; -import { CheckmarkFilled, CheckmarkOutline, CloseFilled, Queued } from '@carbon/react/icons'; import { Loading } from '@carbon/react'; -import styles from './reports.scss'; +import { CheckmarkFilled, CheckmarkOutline, CloseFilled, Queued } from '@carbon/react/icons'; import { COMPLETED, FAILED, PROCESSING, + REQUESTED, SAVED, - SCHEDULED, SCHEDULE_COMPLETED, - REQUESTED, + SCHEDULED, } from './report-statuses-constants'; +import styles from './reports.scss'; interface ReportStatusProps { status: string; @@ -23,25 +24,25 @@ const ReportStatus: React.FC = ({ status }) => { <> {status === SAVED && ( <> - - {t('completedAndPreserved', 'Completed and Preserved')} + + {t('completedAndPreserved', 'Completed and preserved')} )} {status === COMPLETED && ( <> - + {t('completed', 'Completed')} )} {status === PROCESSING && ( <> - + {t('running', 'Running')} )} {status === FAILED && ( <> - + {t('failed', 'Failed')} )} @@ -57,7 +58,7 @@ const ReportStatus: React.FC = ({ status }) => { )} {status === REQUESTED && ( <> - + {t('queued', 'Queued')} )} diff --git a/packages/esm-reports-app/src/components/run-report/cancel-report-modal.component.tsx b/packages/esm-reports-app/src/components/run-report/cancel-report-modal.component.tsx index f1e9ff9..b3659b7 100644 --- a/packages/esm-reports-app/src/components/run-report/cancel-report-modal.component.tsx +++ b/packages/esm-reports-app/src/components/run-report/cancel-report-modal.component.tsx @@ -1,9 +1,9 @@ import React, { useCallback, useState } from 'react'; -import { ModalBody, Button, ModalFooter, ModalHeader, InlineLoading } from '@carbon/react'; import { useTranslation } from 'react-i18next'; -import { cancelReportRequest } from '../reports.resource'; -import { showSnackbar } from '@openmrs/esm-framework'; import { mutate } from 'swr'; +import { ModalBody, Button, ModalFooter, ModalHeader, InlineLoading } from '@carbon/react'; +import { showSnackbar } from '@openmrs/esm-framework'; +import { cancelReportRequest } from '../reports.resource'; import { PROCESSING_REPORT_STATUSES, RAN_REPORT_STATUSES, @@ -22,6 +22,69 @@ const CancelReportModal: React.FC = ({ closeModal, repor const { t } = useTranslation(); const [isCanceling, setIsCanceling] = useState(false); + const getModalTitleByType = useCallback( + (modalType: ModalType) => { + if (modalType === 'delete') { + return t('deleteReport', 'Delete report'); + } else if (modalType === 'cancel') { + return t('cancelReport', 'Cancel report'); + } else if (modalType === 'schedule') { + return t('scheduleReport', 'Schedule report'); + } + }, + [t], + ); + + const getModalBodyByType = useCallback( + (modalType: ModalType) => { + if (modalType === 'delete') { + return t('deleteReportModalText', 'Are you sure you want to delete this report?'); + } else if (modalType === 'cancel') { + return t('cancelReportModalText', 'Are you sure you want to cancel this report?'); + } else if (modalType === 'schedule') { + return t('deleteReportScheduleModalText', 'Are you sure you want to delete this schedule?'); + } + }, + [t], + ); + + const getSuccessToastMessageByType = useCallback( + (modalType: ModalType) => { + if (modalType === 'delete') { + return t('reportDeletedSuccessfully', 'Report deleted successfully'); + } else if (modalType === 'cancel') { + return t('reportCancelledSuccessfully', 'Report cancelled successfully'); + } else if (modalType === 'schedule') { + return t('reportScheduleDeletedSuccessfully', 'Report schedule deleted successfully'); + } + }, + [t], + ); + + const getFailedToastMessageByType = useCallback( + (modalType: ModalType) => { + if (modalType === 'delete') { + return t('reportDeletingErrorMsg', 'Error during report deleting'); + } else if (modalType === 'cancel') { + return t('reportCancelingErrorMsg', 'Error during report canceling'); + } else if (modalType === 'schedule') { + return t('reportScheduleDeletingErrorMsg', 'Error during report schedule deleting'); + } + }, + [t], + ); + + const getLoadingMessageByType = useCallback( + (modalType: ModalType) => { + if (modalType === 'delete' || modalType === 'schedule') { + return t('deleting', 'Deleting'); + } else if (modalType === 'cancel') { + return t('cancelling', 'Cancelling'); + } + }, + [t], + ); + const handleCancel = useCallback(async () => { try { setIsCanceling(true); @@ -42,7 +105,14 @@ const CancelReportModal: React.FC = ({ closeModal, repor } finally { setIsCanceling(false); } - }, [closeModal]); + }, [ + closeModal, + getFailedToastMessageByType, + getModalTitleByType, + getSuccessToastMessageByType, + modalType, + reportRequestUuid, + ]); const callMutates = (modalType: ModalType) => { let baseUrl = '/ws/rest/v1/reportingrest/reportRequest?status='; @@ -56,54 +126,6 @@ const CancelReportModal: React.FC = ({ closeModal, repor } }; - const getModalTitleByType = (modalType: ModalType) => { - if (modalType === 'delete') { - return t('deleteReport', 'Delete report'); - } else if (modalType === 'cancel') { - return t('cancelReport', 'Cancel report'); - } else if (modalType === 'schedule') { - return t('scheduleReport', 'Schedule report'); - } - }; - - const getModalBodyByType = (modalType: ModalType) => { - if (modalType === 'delete') { - return t('deleteReportModalText', 'Are you sure you want to delete this report?'); - } else if (modalType === 'cancel') { - return t('cancelReportModalText', 'Are you sure you want to cancel this report?'); - } else if (modalType === 'schedule') { - return t('deleteReportScheduleModalText', 'Are you sure you want to delete this schedule?'); - } - }; - - const getSuccessToastMessageByType = (modalType: ModalType) => { - if (modalType === 'delete') { - return t('reportDeletedSuccessfully', 'Report deleted successfully'); - } else if (modalType === 'cancel') { - return t('reportCancelledSuccessfully', 'Report cancelled successfully'); - } else if (modalType === 'schedule') { - return t('reportScheduleDeletedSuccessfully', 'Report schedule deleted successfully'); - } - }; - - const getFailedToastMessageByType = (modalType: ModalType) => { - if (modalType === 'delete') { - return t('reportDeletingErrorMsg', 'Error during report deleting'); - } else if (modalType === 'cancel') { - return t('reportCancelingErrorMsg', 'Error during report canceling'); - } else if (modalType === 'schedule') { - return t('reportScheduleDeletingErrorMsg', 'Error during report schedule deleting'); - } - }; - - const getLoadingMessageByType = (modalType: ModalType) => { - if (modalType === 'delete' || modalType === 'schedule') { - return t('deleting', 'Deleting'); - } else if (modalType === 'cancel') { - return t('canceling', 'Canceling'); - } - }; - return (
diff --git a/packages/esm-reports-app/src/components/run-report/run-report-form.component.tsx b/packages/esm-reports-app/src/components/run-report/run-report-form.component.tsx index b6a9635..523c7f3 100644 --- a/packages/esm-reports-app/src/components/run-report/run-report-form.component.tsx +++ b/packages/esm-reports-app/src/components/run-report/run-report-form.component.tsx @@ -1,12 +1,12 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import classNames from 'classnames'; +import { take } from 'rxjs/operators'; import { useTranslation } from 'react-i18next'; -import styles from './run-report-form.scss'; -import { useLocations, useReportDefinitions, useReportDesigns, runReportObservable } from '../reports.resource'; -import { closeOverlay } from '../../hooks/useOverlay'; import { Button, ButtonSet, DatePicker, DatePickerInput, Form, Select, SelectItem, TextInput } from '@carbon/react'; import { showSnackbar, useLayoutType } from '@openmrs/esm-framework'; -import { take } from 'rxjs/operators'; -import classNames from 'classnames'; +import { closeOverlay } from '../../hooks/useOverlay'; +import { useLocations, useReportDefinitions, useReportDesigns, runReportObservable } from '../reports.resource'; +import styles from './run-report-form.scss'; interface RunReportForm { closePanel: () => void; @@ -24,9 +24,14 @@ const RunReportForm: React.FC = ({ closePanel }) => { const { reportDesigns, mutateReportDesigns } = useReportDesigns(reportUuid); + const supportedParameterTypes = useMemo( + () => ['java.util.Date', 'java.lang.String', 'java.lang.Integer', 'org.openmrs.Location'], + [], + ); + useEffect(() => { mutateReportDesigns(); - }, [reportUuid]); + }, [mutateReportDesigns, reportUuid]); useEffect(() => { const paramTypes = currentReport?.parameters.map((param) => param.type); @@ -40,9 +45,7 @@ const RunReportForm: React.FC = ({ closePanel }) => { } else { setIsFormValid(false); } - }, [reportParameters, reportUuid, renderModeUuid]); - - const supportedParameterTypes = ['java.util.Date', 'java.lang.String', 'java.lang.Integer', 'org.openmrs.Location']; + }, [reportParameters, reportUuid, renderModeUuid, currentReport?.parameters, supportedParameterTypes]); const { reportDefinitions } = useReportDefinitions(); const { locations } = useLocations(); @@ -111,7 +114,7 @@ const RunReportForm: React.FC = ({ closePanel }) => { function handleOnChange(event) { const key = event.target.name; let value = null; - if (event.target.type == 'checkbox') { + if (event.target.type === 'checkbox') { value = event.target.checked; } else { value = event.target.value; @@ -153,8 +156,7 @@ const RunReportForm: React.FC = ({ closePanel }) => { setTimeout(() => { showSnackbar({ kind: 'success', - title: t('reportRunning', 'Report running'), - subtitle: t('reportRanSuccessfullyMsg', 'Report ran successfully'), + title: t('reportRanSuccessfully', 'Report ran successfully'), }); closePanel(); setIsSubmitting(false); @@ -164,14 +166,14 @@ const RunReportForm: React.FC = ({ closePanel }) => { console.error(error); showSnackbar({ kind: 'error', - title: t('reportRunningErrorMsg', 'Error while running the report'), - subtitle: t('reportRunningErrorMsg', 'Error while running the report'), + title: t('errorRunningReport', 'Error running report'), + subtitle: error?.message, }); setIsSubmitting(false); }, ); }, - [reportUuid, renderModeUuid, reportParameters], + [closePanel, reportParameters, reportUuid, renderModeUuid, t], ); return ( diff --git a/packages/esm-reports-app/src/components/scheduled-overview-cell-content.component.tsx b/packages/esm-reports-app/src/components/scheduled-overview-cell-content.component.tsx index 7ae7b40..97881fd 100644 --- a/packages/esm-reports-app/src/components/scheduled-overview-cell-content.component.tsx +++ b/packages/esm-reports-app/src/components/scheduled-overview-cell-content.component.tsx @@ -1,15 +1,15 @@ import React from 'react'; import { Edit, TrashCan } from '@carbon/react/icons'; -import { showModal, userHasAccess, useSession } from '@openmrs/esm-framework'; -import ReportScheduleDescription from './report-schedule-description.component'; -import NextReportExecution from './next-report-execution.component'; -import ReportOverviewButton from './report-overview-button.component'; import { useTranslation } from 'react-i18next'; +import { showModal, userHasAccess, useSession } from '@openmrs/esm-framework'; import { closeOverlay, launchOverlay } from '../hooks/useOverlay'; -import styles from './reports.scss'; import { PRIVILEGE_SYSTEM_DEVELOPER } from '../constants'; import EditScheduledReportForm from './edit-scheduled-report/edit-scheduled-report-form.component'; +import NextReportExecution from './next-report-execution.component'; +import ReportOverviewButton from './report-overview-button.component'; +import ReportScheduleDescription from './report-schedule-description.component'; import ScheduledReportStatus from './scheduled-report-status.component'; +import styles from './reports.scss'; interface ScheduledOverviewCellContentProps { cell: { info: { header: string }; value: any }; @@ -40,7 +40,7 @@ const ScheduledOverviewCellContent: React.FC reportRequestUuid={null} onClick={() => { launchOverlay( - t('editScheduledReport', 'Edit Scheduled Report'), + t('editScheduledReport', 'Edit scheduled report'), { const { t } = useTranslation(); @@ -42,7 +42,7 @@ const ScheduledOverviewComponent: React.FC = () => { const { currentPage, results, goTo } = usePagination(scheduledReportRows, pageSize); const tableHeaders = [ - { key: 'name', header: t('reportName', 'Report Name') }, + { key: 'name', header: t('reportName', 'Report name') }, { key: 'status', header: t('status', 'Status') }, { key: 'schedule', header: t('schedule', 'Schedule') }, { key: 'nextRun', header: t('nextRun', 'Next run') }, diff --git a/packages/esm-reports-app/src/components/scheduled-report-status.component.tsx b/packages/esm-reports-app/src/components/scheduled-report-status.component.tsx index d8154f9..7f19845 100644 --- a/packages/esm-reports-app/src/components/scheduled-report-status.component.tsx +++ b/packages/esm-reports-app/src/components/scheduled-report-status.component.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import styles from './reports.scss'; -import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; +import { useTranslation } from 'react-i18next'; +import styles from './reports.scss'; interface ReportStatusProps { hasSchedule: string; @@ -17,7 +17,7 @@ const ScheduledReportStatus: React.FC = ({ hasSchedule }) => [styles.notScheduledStatusText]: !hasSchedule, })} > - {hasSchedule ? t('scheduled', 'Scheduled') : t('notScheduled', 'Not Scheduled')} + {hasSchedule ? t('scheduled', 'Scheduled') : t('notScheduled', 'Not scheduled')} ); }; diff --git a/packages/esm-reports-app/src/components/simple-cron-editor/cron-date-picker.component.tsx b/packages/esm-reports-app/src/components/simple-cron-editor/cron-date-picker.component.tsx index 34897f3..f46e9b0 100644 --- a/packages/esm-reports-app/src/components/simple-cron-editor/cron-date-picker.component.tsx +++ b/packages/esm-reports-app/src/components/simple-cron-editor/cron-date-picker.component.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from 'react'; -import { DatePicker, DatePickerInput } from '@carbon/react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import styles from './simple-cron-editor.scss'; +import { DatePicker, DatePickerInput } from '@carbon/react'; import { isEqual } from 'lodash-es'; +import styles from './simple-cron-editor.scss'; interface CronDatePickerProps { value: Date; @@ -22,27 +22,27 @@ const CronDatePicker: React.FC = ({ value, onChange }) => { invalidText: null, }); + const validate = useCallback(() => { + if (!(valueInternal instanceof Date)) { + setValidationState({ invalid: true, invalidText: t('dateRequired', 'Required') }); + } else { + setValidationState({ invalid: false, invalidText: null }); + } + }, [t, valueInternal]); + useEffect(() => { if (!isEqual(value, valueInternal)) { setValueInternal(value); } - }, [value]); + }, [value, valueInternal]); useEffect(() => { validate(); - }, [valueInternal]); + }, [validate, valueInternal]); useEffect(() => { onChange(validationState.invalid ? null : valueInternal); - }, [validationState]); - - const validate = () => { - if (!(valueInternal instanceof Date)) { - setValidationState({ invalid: true, invalidText: t('dateRequired', 'Required') }); - } else { - setValidationState({ invalid: false, invalidText: null }); - } - }; + }, [onChange, validationState, valueInternal]); return (
@@ -55,7 +55,7 @@ const CronDatePicker: React.FC = ({ value, onChange }) => { setValueInternal(selectedDate); }} > - + {validationState.invalid && ( {validationState.invalidText && t(validationState.invalidText)} diff --git a/packages/esm-reports-app/src/components/simple-cron-editor/cron-day-of-month-select.component.tsx b/packages/esm-reports-app/src/components/simple-cron-editor/cron-day-of-month-select.component.tsx index c1da963..59124e5 100644 --- a/packages/esm-reports-app/src/components/simple-cron-editor/cron-day-of-month-select.component.tsx +++ b/packages/esm-reports-app/src/components/simple-cron-editor/cron-day-of-month-select.component.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { Select, SelectItem } from '@carbon/react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { type CronField, DAYS_OF_MONTH, DAYS_OF_MONTH_DEFAULT_LABELS } from './commons'; +import { Select, SelectItem } from '@carbon/react'; import { isEqual } from 'lodash-es'; +import { type CronField, DAYS_OF_MONTH, DAYS_OF_MONTH_DEFAULT_LABELS } from './commons'; interface CronDayOfMonthSelectProps { value: CronField; @@ -23,27 +23,27 @@ const CronDayOfMonthSelect: React.FC = ({ value, onCh invalidText: null, }); + const validate = useCallback(() => { + if (!!valueInternal) { + setValidationState({ invalid: false, invalidText: null }); + } else { + setValidationState({ invalid: true, invalidText: t('dayOfMonthRequired', 'Required') }); + } + }, [t, valueInternal]); + useEffect(() => { if (!isEqual(value, valueInternal)) { setValueInternal(value?.value); } - }, [value]); + }, [value, valueInternal]); useEffect(() => { validate(); - }, [valueInternal]); + }, [validate, valueInternal]); useEffect(() => { - onChange(validationState.invalid ? null : DAYS_OF_MONTH.find((dayOfMonth) => dayOfMonth.value == valueInternal)); - }, [validationState]); - - const validate = () => { - if (!!valueInternal) { - setValidationState({ invalid: false, invalidText: null }); - } else { - setValidationState({ invalid: true, invalidText: t('dayOfMonthRequired', 'Required') }); - } - }; + onChange(validationState.invalid ? null : DAYS_OF_MONTH.find((dayOfMonth) => dayOfMonth.value === valueInternal)); + }, [onChange, validationState, valueInternal]); const translatedOptions = useMemo( () => @@ -56,7 +56,7 @@ const CronDayOfMonthSelect: React.FC = ({ value, onCh return ( { setEditorState((state) => ({ ...state, scheduleType: event.target.value })); }} @@ -234,7 +251,8 @@ const SimpleCronEditor: React.FC = ({ initialCron, onChan > {SCHEDULE_TYPES.filter( (scheduleType) => - (initialScheduleType != ST_ADVANCED && scheduleType != ST_ADVANCED) || initialScheduleType == ST_ADVANCED, + (initialScheduleType !== ST_ADVANCED && scheduleType !== ST_ADVANCED) || + initialScheduleType === ST_ADVANCED, ).map((scheduleType) => ( = ({ initialCron, onChan
); - }; + }, [editorState.scheduleType, initialScheduleType, t]); - const renderDatePicker = () => { + const renderDatePicker = useCallback(() => { return (
setEditorState((state) => ({ ...state, date: selectedDate }))} value={editorState.date} - onChange={(selectedDate) => { - setEditorState((state) => ({ ...state, date: selectedDate })); - }} />
); - }; + }, [editorState.date]); - const renderDayOfWeekSelect = () => { + const renderDayOfWeekSelect = useCallback(() => { return (
setEditorState((state) => ({ ...state, selectedDaysOfWeek }))} value={editorState.selectedDaysOfWeek} - onChange={(selectedDaysOfWeek) => { - setEditorState((state) => ({ ...state, selectedDaysOfWeek })); - }} />
); - }; + }, [editorState.selectedDaysOfWeek]); - const renderTimePicker = () => { + const renderTimePicker = useCallback(() => { return (
setEditorState((state) => ({ ...state, time: selectedTime }))} value={editorState.time} - onChange={(selectedTime) => { - setEditorState((state) => ({ ...state, time: selectedTime })); - }} />
); - }; + }, [editorState.time]); - const renderDayOfMonthSelect = () => { + const renderDayOfMonthSelect = useCallback(() => { return (
setEditorState((state) => ({ ...state, selectedDayOfMonth }))} value={editorState.selectedDayOfMonth} - onChange={(selectedDayOfMonth) => { - setEditorState((state) => ({ ...state, selectedDayOfMonth })); - }} />
); - }; + }, [editorState.selectedDayOfMonth]); - const renderCronInput = () => { + const renderCronInput = useCallback(() => { return (
setEditorState((state) => ({ ...state, cron: event.target.value }))} + readOnly type="text" - readOnly={true} - hideLabel={true} value={editorState.cron} - onChange={(event) => { - setEditorState((state) => ({ ...state, cron: event.target.value })); - }} />
); - }; + }, [editorState.cron]); return (
{renderScheduleTypeSelect()} - {editorState.scheduleType != ST_ADVANCED && editorState.scheduleType != ST_EVERY_DAY && ( + {editorState.scheduleType !== ST_ADVANCED && editorState.scheduleType !== ST_EVERY_DAY && (
{t('on', 'on')}
)} - {editorState.scheduleType == ST_ONCE && renderDatePicker()} - {editorState.scheduleType == ST_EVERY_WEEK && renderDayOfWeekSelect()} - {editorState.scheduleType == ST_EVERY_MONTH && renderDayOfMonthSelect()} - {editorState.scheduleType != ST_ADVANCED && ( + {editorState.scheduleType === ST_ONCE && renderDatePicker()} + {editorState.scheduleType === ST_EVERY_WEEK && renderDayOfWeekSelect()} + {editorState.scheduleType === ST_EVERY_MONTH && renderDayOfMonthSelect()} + {editorState.scheduleType !== ST_ADVANCED && (
{t('at', 'at')}
)} - {editorState.scheduleType != ST_ADVANCED && renderTimePicker()} - {editorState.scheduleType == ST_ADVANCED && renderCronInput()} + {editorState.scheduleType !== ST_ADVANCED && renderTimePicker()} + {editorState.scheduleType === ST_ADVANCED && renderCronInput()}
); }; diff --git a/packages/esm-reports-app/src/hooks/useOverlay.tsx b/packages/esm-reports-app/src/hooks/useOverlay.ts similarity index 100% rename from packages/esm-reports-app/src/hooks/useOverlay.tsx rename to packages/esm-reports-app/src/hooks/useOverlay.ts index 3716d29..75c0b4d 100644 --- a/packages/esm-reports-app/src/hooks/useOverlay.tsx +++ b/packages/esm-reports-app/src/hooks/useOverlay.ts @@ -1,5 +1,5 @@ -import { getGlobalStore } from '@openmrs/esm-framework'; import { useCallback, useEffect, useState } from 'react'; +import { getGlobalStore } from '@openmrs/esm-framework'; export interface OverlayStore { isOverlayOpen: boolean; diff --git a/packages/esm-reports-app/src/index.ts b/packages/esm-reports-app/src/index.ts index 9ea2fb3..3296f11 100644 --- a/packages/esm-reports-app/src/index.ts +++ b/packages/esm-reports-app/src/index.ts @@ -1,9 +1,3 @@ -/** - * This is the entrypoint file of the application. It communicates the - * important features of this microfrontend to the app shell. It - * connects the app shell to the React application(s) that make up this - * microfrontend. - */ import { getAsyncLifecycle, registerBreadcrumbs } from '@openmrs/esm-framework'; const moduleName = '@openmrs/esm-reports-app'; @@ -13,19 +7,8 @@ const options = { moduleName, }; -/** - * This tells the app shell how to obtain translation files: that they - * are JSON files in the directory `../translations` (which you should - * see in the directory structure). - */ export const importTranslation = require.context('../translations', false, /.json$/, 'lazy'); -/** - * This function performs any setup that should happen at microfrontend - * load-time (such as defining the config schema) and then returns an - * object which describes how the React application(s) should be - * rendered. - */ export function startupApp() { registerBreadcrumbs([ { @@ -50,13 +33,6 @@ export function startupApp() { ]); } -/** - * This named export tells the app shell that the default export of `root.component.tsx` - * should be rendered when the route matches `root`. The full route - * will be `openmrsSpaBase() + 'root'`, which is usually - * `/openmrs/spa/root`. - */ - export const root = getAsyncLifecycle(() => import('./reports.component'), options); export const reportsLink = getAsyncLifecycle(() => import('./reports-link'), options); diff --git a/packages/esm-reports-app/src/reports-link.tsx b/packages/esm-reports-app/src/reports-link.tsx index ca52195..91c91ce 100644 --- a/packages/esm-reports-app/src/reports-link.tsx +++ b/packages/esm-reports-app/src/reports-link.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { spaBasePath } from './constants'; import { ClickableTile, Layer } from '@carbon/react'; import { ArrowRight } from '@carbon/react/icons'; +import { spaBasePath } from './constants'; export default function ReportsLink() { const { t } = useTranslation(); return ( - +
-
{t('manageReports', 'Manage Reports')}
+
{t('manageReports', 'Manage reports')}
{t('reports', 'Reports')}
diff --git a/packages/esm-reports-app/src/reports.component.tsx b/packages/esm-reports-app/src/reports.component.tsx index d60a5aa..3898e5c 100644 --- a/packages/esm-reports-app/src/reports.component.tsx +++ b/packages/esm-reports-app/src/reports.component.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; -import styles from './root.scss'; +import { spaBasePath } from './constants'; import OverviewComponent from './components/overview.component'; import ScheduledOverviewComponent from './components/scheduled-overview.component'; -import { spaBasePath } from './constants'; +import styles from './root.scss'; const RootComponent: React.FC = () => { return ( diff --git a/packages/esm-reports-app/translations/en.json b/packages/esm-reports-app/translations/en.json index 2493f0c..bfa49bb 100644 --- a/packages/esm-reports-app/translations/en.json +++ b/packages/esm-reports-app/translations/en.json @@ -2,12 +2,11 @@ "actions": "Actions", "at": "at", "cancel": "Cancel", - "canceling": "Canceling", + "cancelling": "Cancelling", "cancelReport": "Cancel report", "cancelReportModalText": "Are you sure you want to cancel this report?", - "close": "Close", "completed": "Completed", - "completedAndPreserved": "Completed and Preserved", + "completedAndPreserved": "Completed and preserved", "dateRequired": "Required", "dayOfMonthRequired": "Required", "dayOfWeekRequired": "Required", @@ -18,16 +17,18 @@ "deleteSchedule": "Delete Schedule", "deleting": "Deleting", "download": "Download", - "downloadReport": "Download report(s)", "downloadReports": "Download reports", "edit": "Edit", - "editScheduledReport": "Edit Scheduled Report", + "editScheduledReport": "Edit scheduled report", + "errorDownloadingReport": "Error downloading report", + "errorDownloadingReports": "Error downloading reports", + "errorRunningReport": "Error running report", "failed": "Failed", "nextPage": "Next page", "nextRun": "Next run", "no": "No", "notATimeText": "hh:mm 24-hr pattern required", - "notScheduled": "Not Scheduled", + "notScheduled": "Not scheduled", "on": "on", "outputFormat": "Output format", "parameters": "Parameters", @@ -39,15 +40,12 @@ "reportCancelledSuccessfully": "Report cancelled successfully", "reportDeletedSuccessfully": "Report deleted successfully", "reportDeletingErrorMsg": "Error during report deleting", - "reportDownloadedSuccessfully": "Report(s) downloaded successfully", - "reportDownloadError": "Error during report(s) downloading", - "reportDownloadingErrorMsg": "Error during report(s) downloading", - "reportName": "Report Name", + "reportDownloaded": "Report downloaded", + "reportDownloadedSuccessfully": "Report downloaded successfully", + "reportName": "Report name", "reportPreservedSuccessfully": "Report preserved successfully", "reportPreservingErrorMsg": "Error during report preserving", - "reportRanSuccessfullyMsg": "Report ran successfully", - "reportRunning": "Report running", - "reportRunningErrorMsg": "Error while running the report", + "reportRanSuccessfully": "Report ran successfully", "reports": "Reports", "reportSchedule": "Report schedule", "reportScheduled": "Report scheduled", @@ -55,17 +53,18 @@ "reportScheduleDeletingErrorMsg": "Error during report schedule deleting", "reportScheduledErrorMsg": "Failed to schedule a report", "reportScheduledSuccessfullyMsg": "Report scheduled successfully", + "reportsDownloaded": "Reports downloaded", + "reportsDownloadedSuccessfully": "Reports downloaded successfully", "requestedBy": "Requested by", "requestedOn": "Requested on", "run": "Run", "running": "Running", "runReport": "Run report", "runReports": "Run reports", - "save": "Save", "schedule": "Schedule", - "scheduleCompleted": "Schedule Completed", + "scheduleCompleted": "Schedule completed", "scheduled": "Scheduled", - "scheduledReports": "Scheduled Reports", + "scheduledReports": "Scheduled reports", "scheduleReport": "Schedule report", "selectReportLabel": "Report", "status": "Status", diff --git a/packages/esm-reports-app/tsconfig.json b/packages/esm-reports-app/tsconfig.json index dfe1b86..1bfe3ea 100644 --- a/packages/esm-reports-app/tsconfig.json +++ b/packages/esm-reports-app/tsconfig.json @@ -1,5 +1,4 @@ { "extends": "../../tsconfig.json", "include": ["src/**/*"], - "exclude": [] } diff --git a/packages/esm-reports-app/webpack.config.js b/packages/esm-reports-app/webpack.config.js index f729685..2c74029 100644 --- a/packages/esm-reports-app/webpack.config.js +++ b/packages/esm-reports-app/webpack.config.js @@ -1 +1 @@ -module.exports = require("openmrs/default-webpack-config"); +module.exports = require('openmrs/default-webpack-config'); diff --git a/packages/esm-system-admin-app/src/dashboard/card.component.tsx b/packages/esm-system-admin-app/src/dashboard/card.component.tsx index f93ce81..d59d236 100644 --- a/packages/esm-system-admin-app/src/dashboard/card.component.tsx +++ b/packages/esm-system-admin-app/src/dashboard/card.component.tsx @@ -1,8 +1,6 @@ import React from 'react'; - import { Layer, ClickableTile } from '@carbon/react'; import { ArrowRight } from '@carbon/react/icons'; - import styles from './card.scss'; export interface LinkCardProps extends TileProps { diff --git a/packages/esm-system-admin-app/src/root.component.tsx b/packages/esm-system-admin-app/src/root.component.tsx index 57bcf46..f30d379 100644 --- a/packages/esm-system-admin-app/src/root.component.tsx +++ b/packages/esm-system-admin-app/src/root.component.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; - import { SystemAdministrationDashboard } from './dashboard/index.component'; const RootComponent: React.FC = () => { diff --git a/packages/esm-system-admin-app/tsconfig.json b/packages/esm-system-admin-app/tsconfig.json index 54ce28c..b5c3f9a 100644 --- a/packages/esm-system-admin-app/tsconfig.json +++ b/packages/esm-system-admin-app/tsconfig.json @@ -1,5 +1,5 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*"], - "exclude": ["src/**/*.test.tsx"] + "include": ["src/**/*", "../../tools/setup-tests.ts"], + } diff --git a/prettier.config.js b/prettier.config.js index 2f61d64..99e9f3d 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,8 +1,8 @@ module.exports = { + bracketSpacing: true, printWidth: 120, + semi: true, singleQuote: true, + tabWidth: 2, trailingComma: 'all', - bracketSpacing: true, - parser: 'typescript', - semi: true, }; diff --git a/yarn.lock b/yarn.lock index 9b11be3..caaff39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2775,6 +2775,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.16.3": + version: 7.26.7 + resolution: "@babel/runtime@npm:7.26.7" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10/c7a661a6836b332d9d2e047cba77ba1862c1e4f78cec7146db45808182ef7636d8a7170be9797e5d8fd513180bffb9fa16f6ca1c69341891efec56113cf22bfc + languageName: node + linkType: hard + "@babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.19.0": version: 7.23.2 resolution: "@babel/runtime@npm:7.23.2" @@ -5447,7 +5456,11 @@ __metadata: dayjs: "npm:^1.11.10" dotenv: "npm:^16.4.5" eslint: "npm:^8.57.0" - eslint-plugin-react-hooks: "npm:^4.6.0" + eslint-plugin-import: "npm:^2.31.0" + eslint-plugin-jest-dom: "npm:^5.5.0" + eslint-plugin-playwright: "npm:^2.2.0" + eslint-plugin-react-hooks: "npm:^5.1.0" + eslint-plugin-testing-library: "npm:^7.1.1" husky: "npm:^9.0.11" i18next: "npm:^23.10.0" i18next-parser: "npm:^8.13.0" @@ -7548,6 +7561,13 @@ __metadata: languageName: node linkType: hard +"@rtsao/scc@npm:^1.1.0": + version: 1.1.0 + resolution: "@rtsao/scc@npm:1.1.0" + checksum: 10/17d04adf404e04c1e61391ed97bca5117d4c2767a76ae3e879390d6dec7b317fcae68afbf9e98badee075d0b64fa60f287729c4942021b4d19cd01db77385c01 + languageName: node + linkType: hard + "@semantic-release/error@npm:^2.1.0": version: 2.2.0 resolution: "@semantic-release/error@npm:2.2.0" @@ -8423,6 +8443,13 @@ __metadata: languageName: node linkType: hard +"@types/json5@npm:^0.0.29": + version: 0.0.29 + resolution: "@types/json5@npm:0.0.29" + checksum: 10/4e5aed58cabb2bbf6f725da13421aa50a49abb6bc17bfab6c31b8774b073fa7b50d557c61f961a09a85f6056151190f8ac95f13f5b48136ba5841f7d4484ec56 + languageName: node + linkType: hard + "@types/keyv@npm:^3.1.1, @types/keyv@npm:^3.1.4": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" @@ -8819,6 +8846,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.23.0, @typescript-eslint/scope-manager@npm:^8.15.0": + version: 8.23.0 + resolution: "@typescript-eslint/scope-manager@npm:8.23.0" + dependencies: + "@typescript-eslint/types": "npm:8.23.0" + "@typescript-eslint/visitor-keys": "npm:8.23.0" + checksum: 10/eb4624ccd907c21ca49c4600dec0c447349d7e987cda21181c008dc5ce855590e311efabe73b79b15da0948ce5f63ce0c33613ab4a39ea95578b099b724392e3 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:7.1.0": version: 7.1.0 resolution: "@typescript-eslint/type-utils@npm:7.1.0" @@ -8843,6 +8880,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.23.0": + version: 8.23.0 + resolution: "@typescript-eslint/types@npm:8.23.0" + checksum: 10/e2a68bc6e89226e47e495a91e0614aa5c3c4580b11f7fd99ac6728c1fce92f10755b0d7ade3cf6d3eb1209cd9cd0f29bd742f8dddc394b28bcead7025394eaa2 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:7.1.0": version: 7.1.0 resolution: "@typescript-eslint/typescript-estree@npm:7.1.0" @@ -8862,6 +8906,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.23.0": + version: 8.23.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.23.0" + dependencies: + "@typescript-eslint/types": "npm:8.23.0" + "@typescript-eslint/visitor-keys": "npm:8.23.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^2.0.1" + peerDependencies: + typescript: ">=4.8.4 <5.8.0" + checksum: 10/ddc9790d460bea065eeed3760759c034aef307e72c51b5ec7d869fdc77f18c28354c9e35841b44eebbdc54241bab4154809ae8213d33593a9bff20dc3b247fc3 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:7.1.0": version: 7.1.0 resolution: "@typescript-eslint/utils@npm:7.1.0" @@ -8879,6 +8941,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^8.15.0": + version: 8.23.0 + resolution: "@typescript-eslint/utils@npm:8.23.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:8.23.0" + "@typescript-eslint/types": "npm:8.23.0" + "@typescript-eslint/typescript-estree": "npm:8.23.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10/72588d617ee5b1fa1020d008a7ff714a4a1e0fc1167aa9ff4b8ae71a37b25f43b2d40bca3380c56bb84d4092b6cac8d5d14d74e290e80217175ccf8237faf22a + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:7.1.0": version: 7.1.0 resolution: "@typescript-eslint/visitor-keys@npm:7.1.0" @@ -8889,6 +8966,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.23.0": + version: 8.23.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.23.0" + dependencies: + "@typescript-eslint/types": "npm:8.23.0" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10/fd473849b85e564e31aec64feb3417a4e16e48bf21f1959fbab56258e19c21ef47bbdb523c64a8921cdc82a5083735418890b6f74b564fd1ece305c977a0f7a6 + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" @@ -9593,6 +9680,20 @@ __metadata: languageName: node linkType: hard +"array-includes@npm:^3.1.8": + version: 3.1.8 + resolution: "array-includes@npm:3.1.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + is-string: "npm:^1.0.7" + checksum: 10/290b206c9451f181fb2b1f79a3bf1c0b66bb259791290ffbada760c79b284eef6f5ae2aeb4bcff450ebc9690edd25732c4c73a3c2b340fcc0f4563aed83bf488 + languageName: node + linkType: hard + "array-union@npm:^1.0.1": version: 1.0.2 resolution: "array-union@npm:1.0.2" @@ -9623,6 +9724,44 @@ __metadata: languageName: node linkType: hard +"array.prototype.findlastindex@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.findlastindex@npm:1.2.5" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/7c5c821f357cd53ab6cc305de8086430dd8d7a2485db87b13f843e868055e9582b1fd338f02338f67fc3a1603ceaf9610dd2a470b0b506f9d18934780f95b246 + languageName: node + linkType: hard + +"array.prototype.flat@npm:^1.3.2": + version: 1.3.3 + resolution: "array.prototype.flat@npm:1.3.3" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/f9b992fa0775d8f7c97abc91eb7f7b2f0ed8430dd9aeb9fdc2967ac4760cdd7fc2ef7ead6528fef40c7261e4d790e117808ce0d3e7e89e91514d4963a531cd01 + languageName: node + linkType: hard + +"array.prototype.flatmap@npm:^1.3.2": + version: 1.3.3 + resolution: "array.prototype.flatmap@npm:1.3.3" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/473534573aa4b37b1d80705d0ce642f5933cccf5617c9f3e8a56686e9815ba93d469138e86a1f25d2fe8af999c3d24f54d703ec1fc2db2e6778d46d0f4ac951e + languageName: node + linkType: hard + "array.prototype.reduce@npm:^1.0.6": version: 1.0.7 resolution: "array.prototype.reduce@npm:1.0.7" @@ -12552,7 +12691,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.1.0": +"debug@npm:^3.1.0, debug@npm:^3.2.7": version: 3.2.7 resolution: "debug@npm:3.2.7" dependencies: @@ -12917,6 +13056,15 @@ __metadata: languageName: node linkType: hard +"doctrine@npm:^2.1.0": + version: 2.1.0 + resolution: "doctrine@npm:2.1.0" + dependencies: + esutils: "npm:^2.0.2" + checksum: 10/555684f77e791b17173ea86e2eea45ef26c22219cb64670669c4f4bebd26dbc95cd90ec1f4159e9349a6bb9eb892ce4dde8cd0139e77bedd8bf4518238618474 + languageName: node + linkType: hard + "doctrine@npm:^3.0.0": version: 3.0.0 resolution: "doctrine@npm:3.0.0" @@ -13551,6 +13699,15 @@ __metadata: languageName: node linkType: hard +"es-shim-unscopables@npm:^1.0.2": + version: 1.0.2 + resolution: "es-shim-unscopables@npm:1.0.2" + dependencies: + hasown: "npm:^2.0.0" + checksum: 10/6d3bf91f658a27cc7217cd32b407a0d714393a84d125ad576319b9e83a893bea165cf41270c29e9ceaa56d3cf41608945d7e2a2c31fd51c0009b0c31402b91c7 + languageName: node + linkType: hard + "es-to-primitive@npm:^1.2.1": version: 1.2.1 resolution: "es-to-primitive@npm:1.2.1" @@ -13756,12 +13913,103 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react-hooks@npm:^4.6.0": - version: 4.6.0 - resolution: "eslint-plugin-react-hooks@npm:4.6.0" +"eslint-import-resolver-node@npm:^0.3.9": + version: 0.3.9 + resolution: "eslint-import-resolver-node@npm:0.3.9" + dependencies: + debug: "npm:^3.2.7" + is-core-module: "npm:^2.13.0" + resolve: "npm:^1.22.4" + checksum: 10/d52e08e1d96cf630957272e4f2644dcfb531e49dcfd1edd2e07e43369eb2ec7a7d4423d417beee613201206ff2efa4eb9a582b5825ee28802fc7c71fcd53ca83 + languageName: node + linkType: hard + +"eslint-module-utils@npm:^2.12.0": + version: 2.12.0 + resolution: "eslint-module-utils@npm:2.12.0" + dependencies: + debug: "npm:^3.2.7" + peerDependenciesMeta: + eslint: + optional: true + checksum: 10/dd27791147eca17366afcb83f47d6825b6ce164abb256681e5de4ec1d7e87d8605641eb869298a0dbc70665e2446dbcc2f40d3e1631a9475dd64dd23d4ca5dee + languageName: node + linkType: hard + +"eslint-plugin-import@npm:^2.31.0": + version: 2.31.0 + resolution: "eslint-plugin-import@npm:2.31.0" + dependencies: + "@rtsao/scc": "npm:^1.1.0" + array-includes: "npm:^3.1.8" + array.prototype.findlastindex: "npm:^1.2.5" + array.prototype.flat: "npm:^1.3.2" + array.prototype.flatmap: "npm:^1.3.2" + debug: "npm:^3.2.7" + doctrine: "npm:^2.1.0" + eslint-import-resolver-node: "npm:^0.3.9" + eslint-module-utils: "npm:^2.12.0" + hasown: "npm:^2.0.2" + is-core-module: "npm:^2.15.1" + is-glob: "npm:^4.0.3" + minimatch: "npm:^3.1.2" + object.fromentries: "npm:^2.0.8" + object.groupby: "npm:^1.0.3" + object.values: "npm:^1.2.0" + semver: "npm:^6.3.1" + string.prototype.trimend: "npm:^1.0.8" + tsconfig-paths: "npm:^3.15.0" peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - checksum: 10/3c63134e056a6d98d66e2c475c81f904169db817e89316d14e36269919e31f4876a2588aa0e466ec8ef160465169c627fe823bfdaae7e213946584e4a165a3ac + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + checksum: 10/6b76bd009ac2db0615d9019699d18e2a51a86cb8c1d0855a35fb1b418be23b40239e6debdc6e8c92c59f1468ed0ea8d7b85c817117a113d5cc225be8a02ad31c + languageName: node + linkType: hard + +"eslint-plugin-jest-dom@npm:^5.5.0": + version: 5.5.0 + resolution: "eslint-plugin-jest-dom@npm:5.5.0" + dependencies: + "@babel/runtime": "npm:^7.16.3" + requireindex: "npm:^1.2.0" + peerDependencies: + "@testing-library/dom": ^8.0.0 || ^9.0.0 || ^10.0.0 + eslint: ^6.8.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + peerDependenciesMeta: + "@testing-library/dom": + optional: true + checksum: 10/73aaaa6117abbe3b197bc6b1e45839aaa9c2b4c86e7efbc4ff29f03318ec7f019a8e32652c06c61f06fdb22fb296c068a802a268e7b88aa6b71d3477d949b2c6 + languageName: node + linkType: hard + +"eslint-plugin-playwright@npm:^2.2.0": + version: 2.2.0 + resolution: "eslint-plugin-playwright@npm:2.2.0" + dependencies: + globals: "npm:^13.23.0" + peerDependencies: + eslint: ">=8.40.0" + checksum: 10/f17255ab715a7b57b30080d3b279b4088041bcc4733c4727ba495052ce500a5bd020b4235287da48e4251281d39039e1d55ab37904cbc4522dcf0a1ae6775b39 + languageName: node + linkType: hard + +"eslint-plugin-react-hooks@npm:^5.1.0": + version: 5.1.0 + resolution: "eslint-plugin-react-hooks@npm:5.1.0" + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + checksum: 10/b6778fd9e1940b06868921309e8b269426e17eda555816d4b71def4dcf0572de1199fdb627ac09ce42160b9569a93cd9b0fd81b740ab4df98205461c53997a43 + languageName: node + linkType: hard + +"eslint-plugin-testing-library@npm:^7.1.1": + version: 7.1.1 + resolution: "eslint-plugin-testing-library@npm:7.1.1" + dependencies: + "@typescript-eslint/scope-manager": "npm:^8.15.0" + "@typescript-eslint/utils": "npm:^8.15.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + checksum: 10/48a7a7f93afd16f9cf9cccaf7a1e7ba2e2ea9072d598558ce758d396c7a4d6a71e49b4ec654feef67350141f4f2737d7460c07dbfaed4eb60a09d1c7ceb11558 languageName: node linkType: hard @@ -13792,6 +14040,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^4.2.0": + version: 4.2.0 + resolution: "eslint-visitor-keys@npm:4.2.0" + checksum: 10/9651b3356b01760e586b4c631c5268c0e1a85236e3292bf754f0472f465bf9a856c0ddc261fceace155334118c0151778effafbab981413dbf9288349343fa25 + languageName: node + linkType: hard + "eslint@npm:^8.57.0": version: 8.57.0 resolution: "eslint@npm:8.57.0" @@ -14215,6 +14470,19 @@ __metadata: languageName: node linkType: hard +"fast-glob@npm:^3.3.2": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10/dcc6432b269762dd47381d8b8358bf964d8f4f60286ac6aa41c01ade70bda459ff2001b516690b96d5365f68a49242966112b5d5cc9cd82395fa8f9d017c90ad + languageName: node + linkType: hard + "fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" @@ -15298,7 +15566,7 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.19.0": +"globals@npm:^13.19.0, globals@npm:^13.23.0": version: 13.24.0 resolution: "globals@npm:13.24.0" dependencies: @@ -16703,7 +16971,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.16.0": +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.0": version: 2.16.1 resolution: "is-core-module@npm:2.16.1" dependencies: @@ -18342,6 +18610,17 @@ __metadata: languageName: node linkType: hard +"json5@npm:^1.0.2": + version: 1.0.2 + resolution: "json5@npm:1.0.2" + dependencies: + minimist: "npm:^1.2.0" + bin: + json5: lib/cli.js + checksum: 10/a78d812dbbd5642c4f637dd130954acfd231b074965871c3e28a5bbd571f099d623ecf9161f1960c4ddf68e0cc98dee8bebfdb94a71ad4551f85a1afc94b63f6 + languageName: node + linkType: hard + "json5@npm:^2.1.1, json5@npm:^2.1.2, json5@npm:^2.2.0, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" @@ -19420,7 +19699,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.0": +"micromatch@npm:^4.0.0, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -19563,6 +19842,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348 + languageName: node + linkType: hard + "minimist@npm:^1.2.0, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -20491,6 +20779,18 @@ __metadata: languageName: node linkType: hard +"object.fromentries@npm:^2.0.8": + version: 2.0.8 + resolution: "object.fromentries@npm:2.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + checksum: 10/5b2e80f7af1778b885e3d06aeb335dcc86965e39464671adb7167ab06ac3b0f5dd2e637a90d8ebd7426d69c6f135a4753ba3dd7d0fe2a7030cf718dcb910fd92 + languageName: node + linkType: hard + "object.getownpropertydescriptors@npm:^2.1.8": version: 2.1.8 resolution: "object.getownpropertydescriptors@npm:2.1.8" @@ -20506,6 +20806,17 @@ __metadata: languageName: node linkType: hard +"object.groupby@npm:^1.0.3": + version: 1.0.3 + resolution: "object.groupby@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + checksum: 10/44cb86dd2c660434be65f7585c54b62f0425b0c96b5c948d2756be253ef06737da7e68d7106e35506ce4a44d16aa85a413d11c5034eb7ce5579ec28752eb42d0 + languageName: node + linkType: hard + "object.pick@npm:^1.3.0": version: 1.3.0 resolution: "object.pick@npm:1.3.0" @@ -20515,6 +20826,18 @@ __metadata: languageName: node linkType: hard +"object.values@npm:^1.2.0": + version: 1.2.1 + resolution: "object.values@npm:1.2.1" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/f5ec9eccdefeaaa834b089c525663436812a65ff13de7964a1c3a9110f32054f2d58aa476a645bb14f75a79f3fe1154fb3e7bfdae7ac1e80affe171b2ef74bce + languageName: node + linkType: hard + "obuf@npm:^1.0.0, obuf@npm:^1.1.2": version: 1.1.2 resolution: "obuf@npm:1.1.2" @@ -23418,6 +23741,13 @@ __metadata: languageName: node linkType: hard +"requireindex@npm:^1.2.0": + version: 1.2.0 + resolution: "requireindex@npm:1.2.0" + checksum: 10/266d1cb31f6cbc4b6cf2e898f5bbc45581f7919bcf61bba5c45d0adb69b722b9ff5a13727be3350cde4520d7cd37f39df45d58a29854baaa4552cd6b05ae4a1a + languageName: node + linkType: hard + "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -23492,7 +23822,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.3.2": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.22.4, resolve@npm:^1.3.2": version: 1.22.10 resolution: "resolve@npm:1.22.10" dependencies: @@ -23525,7 +23855,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.12.0#optional!builtin, resolve@patch:resolve@npm%3A^1.3.2#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.12.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A^1.3.2#optional!builtin": version: 1.22.10 resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" dependencies: @@ -24088,6 +24418,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.6.0": + version: 7.7.1 + resolution: "semver@npm:7.7.1" + bin: + semver: bin/semver.js + checksum: 10/4cfa1eb91ef3751e20fc52e47a935a0118d56d6f15a837ab814da0c150778ba2ca4f1a4d9068b33070ea4273629e615066664c2cfcd7c272caf7a8a0f6518b2c + languageName: node + linkType: hard + "semver@npm:~5.3.0": version: 5.3.0 resolution: "semver@npm:5.3.0" @@ -25177,7 +25516,7 @@ __metadata: languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.9": +"string.prototype.trimend@npm:^1.0.8, string.prototype.trimend@npm:^1.0.9": version: 1.0.9 resolution: "string.prototype.trimend@npm:1.0.9" dependencies: @@ -25292,6 +25631,13 @@ __metadata: languageName: node linkType: hard +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 10/8d50ff27b7ebe5ecc78f1fe1e00fcdff7af014e73cf724b46fb81ef889eeb1015fc5184b64e81a2efe002180f3ba431bdd77e300da5c6685d702780fbf0c8d5b + languageName: node + linkType: hard + "strip-bom@npm:^4.0.0": version: 4.0.0 resolution: "strip-bom@npm:4.0.0" @@ -26021,6 +26367,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.0.1": + version: 2.0.1 + resolution: "ts-api-utils@npm:2.0.1" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10/2e68938cd5acad6b5157744215ce10cd097f9f667fd36b5fdd5efdd4b0c51063e855459d835f94f6777bb8a0f334916b6eb5c1eedab8c325feb34baa39238898 + languageName: node + linkType: hard + "ts-easing@npm:^0.2.0": version: 0.2.0 resolution: "ts-easing@npm:0.2.0" @@ -26028,6 +26383,18 @@ __metadata: languageName: node linkType: hard +"tsconfig-paths@npm:^3.15.0": + version: 3.15.0 + resolution: "tsconfig-paths@npm:3.15.0" + dependencies: + "@types/json5": "npm:^0.0.29" + json5: "npm:^1.0.2" + minimist: "npm:^1.2.6" + strip-bom: "npm:^3.0.0" + checksum: 10/2041beaedc6c271fc3bedd12e0da0cc553e65d030d4ff26044b771fac5752d0460944c0b5e680f670c2868c95c664a256cec960ae528888db6ded83524e33a14 + languageName: node + linkType: hard + "tslib@npm:^1.10.0, tslib@npm:^1.9.0": version: 1.14.1 resolution: "tslib@npm:1.14.1"