diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f676172..ed16606 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,6 @@ -name: Node.js CI +name: UgandaEMR CI on: - workflow_dispatch: # enables the workflow to be triggered manually push: branches: [main] pull_request: @@ -9,11 +8,10 @@ on: release: types: - created - env: - ESM_NAME: "@ugandaemr/esm-template-app" - JS_NAME: "esm-ugandaemr-template-app.js" - + ESM_NAME: "@ugandaemr/esm-data-entry-statistics-app" + JS_NAME: "esm-data-entry-statistics-app.js" + jobs: build: runs-on: ubuntu-latest @@ -30,10 +28,17 @@ jobs: node-version: "18.x" registry-url: "https://registry.npmjs.org" + - name: Cache dependencies + id: cache + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + - name: Install dependencies if: steps.cache.outputs.cache-hit != 'true' run: yarn install --immutable - + - name: Setup local cache server for Turborepo uses: felixmosh/turborepo-gh-artifacts@v2 with: @@ -58,55 +63,51 @@ jobs: if: ${{ github.event_name == 'push' }} steps: - - run: echo "Uncomment the lines below and delete this one." - # - uses: actions/checkout@v3 - - # - name: Download Artifacts - # uses: actions/download-artifact@v3 - - # - name: Setup Node.js - # uses: actions/setup-node@v3 - # with: - # node-version: "18.x" - # registry-url: "https://registry.npmjs.org" - - # - name: Cache dependencies - # id: cache - # uses: actions/cache@v3 - # with: - # path: "**/node_modules" - # key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - # - name: Install dependencies - # if: steps.cache.outputs.cache-hit != 'true' - # run: yarn install --immutable + - uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18.x" + registry-url: "https://registry.npmjs.org" + + - name: Cache dependencies + id: cache + uses: actions/cache@v3 + with: + path: "**/node_modules" + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: yarn install --immutable - # - name: Setup local cache server for Turborepo - # uses: felixmosh/turborepo-gh-artifacts@v2 - # with: - # repo-token: ${{ secrets.GITHUB_TOKEN }} - # server-token: ${{ secrets.TURBO_SERVER_TOKEN }} + - name: Setup local cache server for Turborepo + uses: felixmosh/turborepo-gh-artifacts@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + server-token: ${{ secrets.TURBO_SERVER_TOKEN }} - # - name: Version - # run: yarn version "$(node -e "console.log(require('semver').inc(require('./package.json').version, 'patch'))")-pre.${{ github.run_number }}" + - name: Version + run: yarn version "$(node -e "console.log(require('semver').inc(require('./package.json').version, 'patch'))")-pre.${{ github.run_number }}" - # - name: Build - # run: yarn turbo build --color --concurrency=5 + - name: Build + run: yarn turbo build --color --concurrency=5 - # - run: git config user.email "<>" && git config user.name "UgandaEMR CI" - # - run: git add . && git commit -m "Prerelease version" --no-verify + - run: git config user.email "<>" && git config user.name "UgandaEMR CI" + - run: git add . && git commit -m "Prerelease version" --no-verify - # - name: Pre-release - # run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn npm publish --access public --tag next - # env: - # NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} + - name: Pre-release + run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn npm publish --access public --tag next + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} - # - name: Upload Artifacts - # uses: actions/upload-artifact@v3 - # with: - # name: dist - # path: | - # dist + - name: Upload Artifacts + uses: actions/upload-artifact@v3 + with: + name: dist + path: | + dist release: runs-on: ubuntu-latest @@ -119,11 +120,12 @@ jobs: - uses: actions/checkout@v3 - name: Download Artifacts uses: actions/download-artifact@v3 - - name: Use Node.js + + - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: "18.x" - registry-url: 'https://registry.npmjs.org' + registry-url: "https://registry.npmjs.org" - name: Cache dependencies id: cache @@ -131,27 +133,17 @@ jobs: with: path: "**/node_modules" key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - + - name: Install dependencies if: steps.cache.outputs.cache-hit != 'true' run: yarn install --immutable - + + - name: Setup local cache server for Turborepo + uses: felixmosh/turborepo-gh-artifacts@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + server-token: ${{ secrets.TURBO_SERVER_TOKEN }} + - run: yarn config set npmAuthToken "${NODE_AUTH_TOKEN}" && yarn npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} - - deploy: - runs-on: ubuntu-latest - - needs: pre_release - - if: ${{ github.event_name == 'push' }} - - steps: - - run: echo "Uncomment the lines below and delete this one." - # - name: Trigger RefApp Build - # uses: fjogeleit/http-request-action@master - # with: - # url: https://ci.openmrs.org/rest/api/latest/queue/REFAPP-D3X - # method: "POST" - # customHeaders: '{ "Authorization": "Bearer ${{ secrets.BAMBOO_TOKEN }}" }' \ No newline at end of file diff --git a/package.json b/package.json index e8729bc..565b62e 100644 --- a/package.json +++ b/package.json @@ -1,104 +1,104 @@ { - "name": "@ugandaemr/esm-template-app", - "version": "1.0.0", - "license": "MPL-2.0", - "description": "A template for creating frontend modules for UgandaEMR", - "browser": "dist/esm-ugandaemr-template-app.js", - "main": "src/index.ts", - "source": true, - "scripts": { - "start": "openmrs develop", - "serve": "webpack serve --mode=development", - "build": "webpack --mode production", - "analyze": "webpack --mode=production --env analyze=true", - "lint": "TIMING=1 eslint src --ext js,jsx,ts,tsx", - "prettier": "prettier --write \"src/**/*.{ts,tsx}\"", - "typescript": "tsc", - "test": "jest --config jest.config.js --passWithNoTests", - "verify": "turbo lint typescript coverage", - "coverage": "yarn test --coverage", - "prepare": "husky install", - "extract-translations": "i18next 'src/**/*.component.tsx' --config ./i18next-parser.config.js" - }, - "husky": { - "hooks": { - "pre-commit": "pretty-quick --staged && yarn verify" - } - }, - "browserslist": [ - "extends browserslist-config-openmrs" - ], - "keywords": [ - "openmrs", - "microfrontends" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/mets-programme/esm-ugandaemr-template-app.git" - }, - "homepage": "https://github.com/mets-programme/esm-ugandaemr-template-app#readme", - "publishConfig": { - "access": "public" - }, - "bugs": { - "url": "https://github.com/mets-programme/esm-ugandaemr-template-app/issues" - }, - "dependencies": { - "@carbon/react": "^1.33.1", - "lodash-es": "^4.17.21", - "react-image-annotate": "^1.8.0" - }, - "peerDependencies": { - "@openmrs/esm-framework": "*", - "dayjs": "1.x", - "react": "18.x", - "react-i18next": "11.x", - "react-router-dom": "6.x", - "rxjs": "6.x" - }, - "devDependencies": { - "@openmrs/esm-framework": "next", - "@openmrs/esm-styleguide": "next", - "@swc/cli": "^0.1.62", - "@swc/core": "^1.3.68", - "@swc/jest": "^0.2.26", - "@testing-library/dom": "^8.20.1", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^14.4.3", - "@types/jest": "^28.1.8", - "@types/react": "^18.2.14", - "@types/react-dom": "^18.2.6", - "@types/react-router": "^5.1.20", - "@types/react-router-dom": "^5.3.3", - "@types/webpack-env": "^1.18.1", - "@typescript-eslint/eslint-plugin": "^5.61.0", - "@typescript-eslint/parser": "^5.61.0", - "css-loader": "^6.8.1", - "eslint": "^8.44.0", - "eslint-config-prettier": "^8.8.0", - "eslint-config-ts-react-important-stuff": "^3.0.0", - "eslint-plugin-prettier": "^4.2.1", - "husky": "^8.0.0", - "i18next": "^23.2.8", - "i18next-parser": "^8.0.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^28.1.3", - "jest-cli": "^28.1.3", - "jest-environment-jsdom": "^28.1.3", - "openmrs": "next", - "prettier": "^2.8.8", - "pretty-quick": "^3.1.3", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-i18next": "^11.18.6", - "react-router-dom": "^6.14.1", - "rxjs": "^6.6.7", - "swc-loader": "^0.2.3", - "turbo": "^1.10.7", - "typescript": "^4.9.5", - "webpack": "^5.88.1", - "webpack-cli": "^5.1.4" - }, - "packageManager": "yarn@3.6.2" + "name": "@ugandaemr/esm-data-entry-statistics-app", + "version": "1.0.0", + "license": "MPL-2.0", + "description": "Frontend module for data entry statistics", + "browser": "dist/esm-ugandaemr-template-app.js", + "main": "src/index.ts", + "source": true, + "scripts": { + "start": "openmrs develop --backend http://167.71.32.250:8080/", + "serve": "webpack serve --mode=development", + "build": "webpack --mode production", + "analyze": "webpack --mode=production --env analyze=true", + "lint": "TIMING=1 eslint src --ext js,jsx,ts,tsx", + "prettier": "prettier --write \"src/**/*.{ts,tsx}\"", + "typescript": "tsc", + "test": "jest --config jest.config.js --passWithNoTests", + "verify": "turbo lint typescript coverage", + "coverage": "yarn test --coverage", + "prepare": "husky install", + "extract-translations": "i18next 'src/**/*.component.tsx' --config ./i18next-parser.config.js" + }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged && yarn verify" + } + }, + "browserslist": [ + "extends browserslist-config-openmrs" + ], + "keywords": [ + "openmrs", + "microfrontends" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/METS-Programme/esm-data-entry-statistics-app.git" + }, + "homepage": "https://github.com/METS-Programme/esm-data-entry-statistics-app/blob/main/README.md", + "publishConfig": { + "access": "public" + }, + "bugs": { + "url": "https://github.com/METS-Programme/esm-data-entry-statistics-app/issues" + }, + "dependencies": { + "@carbon/react": "^1.33.1", + "lodash-es": "^4.17.21", + "react-image-annotate": "^1.8.0" + }, + "peerDependencies": { + "@openmrs/esm-framework": "*", + "dayjs": "1.x", + "react": "18.x", + "react-i18next": "11.x", + "react-router-dom": "6.x", + "rxjs": "6.x" + }, + "devDependencies": { + "@openmrs/esm-framework": "next", + "@openmrs/esm-styleguide": "next", + "@swc/cli": "^0.1.62", + "@swc/core": "^1.3.68", + "@swc/jest": "^0.2.26", + "@testing-library/dom": "^8.20.1", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^14.4.3", + "@types/jest": "^28.1.8", + "@types/react": "^18.2.14", + "@types/react-dom": "^18.2.6", + "@types/react-router": "^5.1.20", + "@types/react-router-dom": "^5.3.3", + "@types/webpack-env": "^1.18.1", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", + "css-loader": "^6.8.1", + "eslint": "^8.44.0", + "eslint-config-prettier": "^8.8.0", + "eslint-config-ts-react-important-stuff": "^3.0.0", + "eslint-plugin-prettier": "^4.2.1", + "husky": "^8.0.0", + "i18next": "^23.2.8", + "i18next-parser": "^8.0.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^28.1.3", + "jest-cli": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "openmrs": "next", + "prettier": "^2.8.8", + "pretty-quick": "^3.1.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-i18next": "^11.18.6", + "react-router-dom": "^6.14.1", + "rxjs": "^6.6.7", + "swc-loader": "^0.2.3", + "turbo": "^1.10.7", + "typescript": "^4.9.5", + "webpack": "^5.88.1", + "webpack-cli": "^5.1.4" + }, + "packageManager": "yarn@3.6.2" } diff --git a/src/components/data-entry-statistics-header/data-entry-statistics-header.component.tsx b/src/components/data-entry-statistics-header/data-entry-statistics-header.component.tsx new file mode 100644 index 0000000..a455be6 --- /dev/null +++ b/src/components/data-entry-statistics-header/data-entry-statistics-header.component.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Calendar, Location } from "@carbon/react/icons"; +import { formatDate, useSession } from "@openmrs/esm-framework"; +import DataEntyStatisticsIllustration from "./data-entry-statistics-illustration.component"; +import styles from "./data-entry-statistics-header.scss"; + +const DatEntryStatisticsHeader: React.FC<{ title?: string }> = ({ title }) => { + const { t } = useTranslation(); + const userSession = useSession(); + const userLocation = userSession?.sessionLocation?.display; + + return ( + <> +
+
+ +
+

{t("statistics", "Statistics")}

+

+ {title ?? t("statistics", "Data Entry Statistics")} +

+
+
+
+
+ + {userLocation} + · + + + {formatDate(new Date(), { mode: "standard" })} + +
+
+
+ + ); +}; + +export default DatEntryStatisticsHeader; diff --git a/src/components/data-entry-statistics-header/data-entry-statistics-header.scss b/src/components/data-entry-statistics-header/data-entry-statistics-header.scss new file mode 100644 index 0000000..b669e10 --- /dev/null +++ b/src/components/data-entry-statistics-header/data-entry-statistics-header.scss @@ -0,0 +1,68 @@ +@use '@carbon/styles/scss/colors'; +@use '@carbon/styles/scss/type'; +@use '@carbon/styles/scss/spacing'; +.header { + @include type.type-style('body-compact-02'); + color: colors.$gray-70; + height: spacing.$spacing-12; + background-color: colors.$white-0; + border-bottom: 1px solid colors.$gray-20; + display: flex; + justify-content: space-between; +} + +.left-justified-items { + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; +} + +.right-justified-items { + @include type.type-style('body-compact-02'); + color: colors.$gray-70; + margin: 0.5rem; +} + +.page-name { + @include type.type-style('heading-04'); +} + +.page-labels { + p:first-of-type { + margin-bottom: 0.25rem; + } +} + +.date-and-location { + display: flex; + justify-content: flex-end; + align-items: center; + margin-right: 1rem; +} + +.value { + margin-left: 0.25rem; +} + +.middot { + margin: 0 0.5rem; +} + +.view { + @include type.type-style('label-01'); +} + +.dropdown { + display: flex; + align-items: center; + justify-content: flex-end; + margin-top: 0.25rem; + :global(.cds--dropdown__wrapper--inline) { + align-items: center; + display: flex; + } + :global(.cds--list-box__menu) { + left: -10.15rem; + } +} \ No newline at end of file diff --git a/src/components/data-entry-statistics-header/data-entry-statistics-illustration.component.tsx b/src/components/data-entry-statistics-header/data-entry-statistics-illustration.component.tsx new file mode 100644 index 0000000..5b44a8e --- /dev/null +++ b/src/components/data-entry-statistics-header/data-entry-statistics-illustration.component.tsx @@ -0,0 +1,27 @@ +import React from "react"; + +const DataEntyStatisticsIllustration: React.FC = () => { + return ( + + Patient queue illustration + + + + + + + ); +}; + +export default DataEntyStatisticsIllustration; diff --git a/src/createDashboardLink.tsx b/src/createDashboardLink.tsx new file mode 100644 index 0000000..59a31ca --- /dev/null +++ b/src/createDashboardLink.tsx @@ -0,0 +1,44 @@ +import { ConfigurableLink } from "@openmrs/esm-framework"; +import React, { useMemo } from "react"; +import { BrowserRouter, useLocation } from "react-router-dom"; + +export interface DashboardLinkConfig { + name: string; + title: string; + slot?: string; +} + +function DashboardExtension({ + dashboardLinkConfig, +}: { + dashboardLinkConfig: DashboardLinkConfig; +}) { + const { name, title } = dashboardLinkConfig; + const location = useLocation(); + const spaBasePath = `${window.spaBase}/home`; + + const navLink = useMemo(() => { + const pathArray = location.pathname.split("/home"); + const lastElement = pathArray[pathArray.length - 1]; + return decodeURIComponent(lastElement); + }, [location.pathname]); + + return ( + + {title} + + ); +} + +export const createDashboardLink = + (dashboardLinkConfig: DashboardLinkConfig) => () => + ( + + + + ); diff --git a/src/data-entry-statistics.component.tsx b/src/data-entry-statistics.component.tsx new file mode 100644 index 0000000..5121df6 --- /dev/null +++ b/src/data-entry-statistics.component.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import DataEntryStatisticsHeader from "./components/data-entry-statistics-header/data-entry-statistics-header.component"; + +const DataEntryStatistics: React.FC = () => { + return ( +
+ +
+ ); +}; + +export default DataEntryStatistics; diff --git a/src/index.ts b/src/index.ts index a96b4da..a48713a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,14 @@ -import { getAsyncLifecycle, defineConfigSchema } from "@openmrs/esm-framework"; +import { + getAsyncLifecycle, + getSyncLifecycle, + defineConfigSchema, +} from "@openmrs/esm-framework"; import { configSchema } from "./config-schema"; +import { createDashboardLink } from "./createDashboardLink"; -const moduleName = "@ugandaemr/esm-template-app"; - +const moduleName = "@ugandaemr/esm-data-entry-statistics-app"; const options = { - featureName: "root", + featureName: "data-entry-statistics-app", moduleName, }; @@ -15,11 +19,24 @@ export const importTranslation = require.context( "lazy" ); -export function startupApp() { - defineConfigSchema(moduleName, configSchema); -} - export const root = getAsyncLifecycle( () => import("./root.component"), options ); + +export const dataEntryStatisticsAppDashboardLink = getSyncLifecycle( + createDashboardLink({ + name: "statistics", + slot: "data-entry-statistics-slot", + title: "Statistics", + }), + options +); +export const dataEntryStatisticsComponent = getAsyncLifecycle( + () => import("./data-entry-statistics.component"), + options +); + +export function startupApp() { + defineConfigSchema(moduleName, configSchema); +} diff --git a/src/root.component.tsx b/src/root.component.tsx index d4fde0f..b93191b 100644 --- a/src/root.component.tsx +++ b/src/root.component.tsx @@ -1,37 +1,14 @@ import React from "react"; -import logo from "./assets/images/logo.svg"; -import styles from "./root.scss"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import DataEntryStatistics from "./data-entry-statistics.component"; const Root: React.FC = () => { return ( -
- Uganda EMR logo -

Welcome to the template app

-

- Use this template as a starter for set up custom UgandaEMR frontend - modules. -

-
-

Next steps

- - -
-
+ + + } /> + + ); }; diff --git a/src/routes.json b/src/routes.json index bb54ff6..3353cb3 100644 --- a/src/routes.json +++ b/src/routes.json @@ -1,13 +1,29 @@ { - "$schema": "https://json.openmrs.org/routes.schema.json", - "backendDependencies": { - "fhir2": "^1.2.0", - "webservices.rest": "^2.24.0" - }, - "pages": [ - { - "component": "root", - "route": "root" - } - ] -} + "$schema": "https://json.openmrs.org/routes.schema.json", + "backendDependencies": { + "fhir2": "^1.2.0", + "webservices.rest": "^2.24.0" + }, + "pages": [{ + "component": "root", + "route": "statistics" + }], + "extensions": [{ + "name": "data-entry-statistics-dashboard", + "slot": "data-entry-statistics-slot", + "component": "root" + }, { + "name": "data-entry-statistics-link", + "component": "dataEntryStatisticsAppDashboardLink", + "slot": "homepage-dashboard-slot", + "order": 3, + "meta": { + "name": "statistics", + "slot": "data-entry-statistics-slot", + "title": "Statistics" + + } + } + + ] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 631c8e8..c427264 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5193,9 +5193,9 @@ __metadata: languageName: node linkType: hard -"@ugandaemr/esm-template-app@workspace:.": +"@ugandaemr/esm-data-entry-statistics-app@workspace:.": version: 0.0.0-use.local - resolution: "@ugandaemr/esm-template-app@workspace:." + resolution: "@ugandaemr/esm-data-entry-statistics-app@workspace:." dependencies: "@carbon/react": ^1.33.1 "@openmrs/esm-framework": next