From a9d872834b7d1dbfc5a616258ce68ae5979887ad Mon Sep 17 00:00:00 2001 From: hridya-egov <153486292+hridya-egov@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:53:25 +0530 Subject: [PATCH] UI core mfe (#1081) * duplicatedthe core module * removed the topbar and sidebar component * added the ui module * removed the css * added css in index.html file to remove the topbar and sidebbar space * added the core module * changed the package name to ui * Update lerna.json * Update package.json * Update index.js * Create mfe-ui-docker.yml --------- Co-authored-by: Jagankumar <53823168+jagankumar-egov@users.noreply.github.com> --- .github/workflows/mfe-ui-docker.yml | 49 ++ micro-ui/web/lerna.json | 4 +- micro-ui/web/package.json | 6 +- .../src/components/TopBarSideBar/index.js | 4 +- micro-ui/web/packages/ui/.babelrc | 6 + micro-ui/web/packages/ui/.gitattributes | 2 + micro-ui/web/packages/ui/.gitignore | 63 ++ micro-ui/web/packages/ui/LICENSE | 21 + micro-ui/web/packages/ui/README.md | 1 + micro-ui/web/packages/ui/docker/Dockerfile | 47 ++ micro-ui/web/packages/ui/docker/nginx.conf | 12 + micro-ui/web/packages/ui/package.json | 64 ++ micro-ui/web/packages/ui/public/index.html | 126 ++++ micro-ui/web/packages/ui/src/App.js | 81 ++ micro-ui/web/packages/ui/src/Module.js | 113 +++ micro-ui/web/packages/ui/src/bootstrap.js | 81 ++ .../packages/ui/src/components/AppModules.js | 54 ++ .../packages/ui/src/components/Background.js | 7 + .../packages/ui/src/components/ChangeCity.js | 92 +++ .../ui/src/components/ChangeLanguage.js | 53 ++ .../ui/src/components/Dialog/LogoutDialog.js | 114 +++ .../ui/src/components/ErrorBoundaries.js | 57 ++ .../ui/src/components/ErrorComponent.js | 46 ++ .../web/packages/ui/src/components/Header.js | 20 + .../web/packages/ui/src/components/Home.js | 147 ++++ .../web/packages/ui/src/components/Loader.js | 11 + .../Search/MobileSearchApplication.js | 174 +++++ .../ui/src/components/Search/SearchFields.js | 29 + .../ui/src/components/Search/index.js | 208 ++++++ .../TopBarSideBar/SideBar/CitizenSideBar.js | 249 +++++++ .../TopBarSideBar/SideBar/EmployeeSideBar.js | 144 ++++ .../SideBar/StaticCitizenSideBar.js | 247 ++++++ .../TopBarSideBar/SideBar/SubMenu.js | 193 +++++ .../components/TopBarSideBar/SideBar/index.js | 23 + .../ui/src/components/TopBarSideBar/TopBar.js | 156 ++++ .../ui/src/components/TopBarSideBar/index.js | 79 ++ .../web/packages/ui/src/components/index.js | 7 + .../web/packages/ui/src/components/utils.js | 22 + .../packages/ui/src/config/sidebar-menu.js | 33 + micro-ui/web/packages/ui/src/context/index.js | 3 + micro-ui/web/packages/ui/src/hooks/index.js | 28 + micro-ui/web/packages/ui/src/hooks/trans.js | 63 ++ micro-ui/web/packages/ui/src/hooks/useAuth.js | 31 + .../web/packages/ui/src/hooks/useInterval.js | 20 + .../packages/ui/src/hooks/useLocalization.js | 113 +++ .../web/packages/ui/src/hooks/useRouter.js | 17 + micro-ui/web/packages/ui/src/index.js | 1 + micro-ui/web/packages/ui/src/locale/i18n.js | 42 ++ .../ui/src/pages/citizen/AllServices/index.js | 26 + .../ui/src/pages/citizen/FAQs/FAQs.js | 39 + .../ui/src/pages/citizen/FAQs/FaqComponent.js | 27 + .../citizen/Home/ImageUpload/UploadDrawer.js | 96 +++ .../pages/citizen/Home/LanguageSelection.js | 48 ++ .../pages/citizen/Home/LocationSelection.js | 64 ++ .../ui/src/pages/citizen/Home/UserProfile.js | 704 ++++++++++++++++++ .../ui/src/pages/citizen/Home/index.js | 173 +++++ .../pages/citizen/HowItWorks/howItWorks.js | 123 +++ .../pages/citizen/Login/SelectMobileNumber.js | 18 + .../ui/src/pages/citizen/Login/SelectName.js | 8 + .../ui/src/pages/citizen/Login/SelectOtp.js | 51 ++ .../ui/src/pages/citizen/Login/config.js | 52 ++ .../ui/src/pages/citizen/Login/index.js | 274 +++++++ .../ui/src/pages/citizen/SearchApp.js | 88 +++ .../StaticDynamicCard.js | 269 +++++++ .../packages/ui/src/pages/citizen/index.js | 228 ++++++ .../employee/ChangePassword/changePassword.js | 168 +++++ .../pages/employee/ChangePassword/config.js | 28 + .../pages/employee/ChangePassword/index.js | 33 + .../pages/employee/ForgotPassword/config.js | 23 + .../employee/ForgotPassword/forgotPassword.js | 142 ++++ .../pages/employee/ForgotPassword/index.js | 33 + .../pages/employee/LanguageSelection/index.js | 61 ++ .../ui/src/pages/employee/Login/config.js | 46 ++ .../ui/src/pages/employee/Login/index.js | 52 ++ .../ui/src/pages/employee/Login/login.js | 158 ++++ .../packages/ui/src/pages/employee/index.js | 138 ++++ micro-ui/web/packages/ui/src/pages/index.js | 83 +++ .../packages/ui/src/redux/reducers/index.js | 8 + micro-ui/web/packages/ui/src/redux/store.js | 24 + micro-ui/web/packages/ui/src/utils/index.js | 3 + .../packages/ui/src/utils/registerRemotes.js | 136 ++++ micro-ui/web/packages/ui/webpack.common.js | 30 + micro-ui/web/packages/ui/webpack.dev.js | 66 ++ micro-ui/web/packages/ui/webpack.prod.js | 39 + 84 files changed, 6717 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/mfe-ui-docker.yml create mode 100644 micro-ui/web/packages/ui/.babelrc create mode 100644 micro-ui/web/packages/ui/.gitattributes create mode 100644 micro-ui/web/packages/ui/.gitignore create mode 100644 micro-ui/web/packages/ui/LICENSE create mode 100644 micro-ui/web/packages/ui/README.md create mode 100644 micro-ui/web/packages/ui/docker/Dockerfile create mode 100644 micro-ui/web/packages/ui/docker/nginx.conf create mode 100644 micro-ui/web/packages/ui/package.json create mode 100644 micro-ui/web/packages/ui/public/index.html create mode 100644 micro-ui/web/packages/ui/src/App.js create mode 100644 micro-ui/web/packages/ui/src/Module.js create mode 100644 micro-ui/web/packages/ui/src/bootstrap.js create mode 100644 micro-ui/web/packages/ui/src/components/AppModules.js create mode 100644 micro-ui/web/packages/ui/src/components/Background.js create mode 100644 micro-ui/web/packages/ui/src/components/ChangeCity.js create mode 100644 micro-ui/web/packages/ui/src/components/ChangeLanguage.js create mode 100644 micro-ui/web/packages/ui/src/components/Dialog/LogoutDialog.js create mode 100644 micro-ui/web/packages/ui/src/components/ErrorBoundaries.js create mode 100644 micro-ui/web/packages/ui/src/components/ErrorComponent.js create mode 100644 micro-ui/web/packages/ui/src/components/Header.js create mode 100644 micro-ui/web/packages/ui/src/components/Home.js create mode 100644 micro-ui/web/packages/ui/src/components/Loader.js create mode 100644 micro-ui/web/packages/ui/src/components/Search/MobileSearchApplication.js create mode 100644 micro-ui/web/packages/ui/src/components/Search/SearchFields.js create mode 100644 micro-ui/web/packages/ui/src/components/Search/index.js create mode 100644 micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/CitizenSideBar.js create mode 100644 micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/EmployeeSideBar.js create mode 100644 micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/StaticCitizenSideBar.js create mode 100644 micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/SubMenu.js create mode 100644 micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/index.js create mode 100644 micro-ui/web/packages/ui/src/components/TopBarSideBar/TopBar.js create mode 100644 micro-ui/web/packages/ui/src/components/TopBarSideBar/index.js create mode 100644 micro-ui/web/packages/ui/src/components/index.js create mode 100644 micro-ui/web/packages/ui/src/components/utils.js create mode 100644 micro-ui/web/packages/ui/src/config/sidebar-menu.js create mode 100644 micro-ui/web/packages/ui/src/context/index.js create mode 100644 micro-ui/web/packages/ui/src/hooks/index.js create mode 100644 micro-ui/web/packages/ui/src/hooks/trans.js create mode 100644 micro-ui/web/packages/ui/src/hooks/useAuth.js create mode 100644 micro-ui/web/packages/ui/src/hooks/useInterval.js create mode 100644 micro-ui/web/packages/ui/src/hooks/useLocalization.js create mode 100644 micro-ui/web/packages/ui/src/hooks/useRouter.js create mode 100644 micro-ui/web/packages/ui/src/index.js create mode 100644 micro-ui/web/packages/ui/src/locale/i18n.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/AllServices/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/FAQs/FAQs.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/FAQs/FaqComponent.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Home/ImageUpload/UploadDrawer.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Home/LanguageSelection.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Home/LocationSelection.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Home/UserProfile.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Home/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/HowItWorks/howItWorks.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Login/SelectMobileNumber.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Login/SelectName.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Login/SelectOtp.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Login/config.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/Login/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/SearchApp.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/StaticDynamicComponent/StaticDynamicCard.js create mode 100644 micro-ui/web/packages/ui/src/pages/citizen/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/ChangePassword/changePassword.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/ChangePassword/config.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/ChangePassword/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/config.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/forgotPassword.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/LanguageSelection/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/Login/config.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/Login/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/Login/login.js create mode 100644 micro-ui/web/packages/ui/src/pages/employee/index.js create mode 100644 micro-ui/web/packages/ui/src/pages/index.js create mode 100644 micro-ui/web/packages/ui/src/redux/reducers/index.js create mode 100644 micro-ui/web/packages/ui/src/redux/store.js create mode 100644 micro-ui/web/packages/ui/src/utils/index.js create mode 100644 micro-ui/web/packages/ui/src/utils/registerRemotes.js create mode 100644 micro-ui/web/packages/ui/webpack.common.js create mode 100644 micro-ui/web/packages/ui/webpack.dev.js create mode 100644 micro-ui/web/packages/ui/webpack.prod.js diff --git a/.github/workflows/mfe-ui-docker.yml b/.github/workflows/mfe-ui-docker.yml new file mode 100644 index 00000000000..92d61133b16 --- /dev/null +++ b/.github/workflows/mfe-ui-docker.yml @@ -0,0 +1,49 @@ +name: ui service docker Image CI + +on: + push: + branches: [ "develop-mfe","develop-mfe-microplan-common-pkgs","develop-mfe-campaign-microplan","build-fix-develop-mfe-campaign-microplan" ] + pull_request: + branches: [ "develop-mfe","develop-mfe-microplan-common-pkgs","develop-mfe-campaign-microplan","build-fix-develop-mfe-campaign-microplan" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Fetch all history for tags and branches + + - name: Set up environment variables + id: env + run: | + echo "BRANCH_NAME=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV + echo "ACTION_NUMBER=${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV + echo "COMMIT_ID=${GITHUB_SHA: -8}" >> $GITHUB_ENV # Extract last 8 characters of SHA + + - name: Build the Docker image + id: docker_build + working-directory: ./micro-ui/web/packages/core + run: | + IMAGE_TAG=egovio/ui:${{ env.BRANCH_NAME }}-${{ env.COMMIT_ID }}-${{ env.ACTION_NUMBER }} + docker build . \ + --file docker/Dockerfile \ + --tag $IMAGE_TAG + echo "::set-output name=image_name::$IMAGE_TAG" + + - name: Login to Docker Hub and Push Docker Image + working-directory: ./micro-ui/web/packages/core + env: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + IMAGE_NAME: ${{ steps.docker_build.outputs.image_name }} + run: | + # Authenticate with Docker Hub + echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + + # Push the image to Docker Hub + docker push $IMAGE_NAME + echo "Docker image pushed: $IMAGE_NAME" diff --git a/micro-ui/web/lerna.json b/micro-ui/web/lerna.json index 23b204dc476..85e4207d790 100644 --- a/micro-ui/web/lerna.json +++ b/micro-ui/web/lerna.json @@ -8,7 +8,9 @@ "packages/components", "packages/svg-components", "packages/microplan", - "packages/campaign" + "packages/campaign", + "packages/hrms", + "packages/ui" ], "command": { "run": { diff --git a/micro-ui/web/package.json b/micro-ui/web/package.json index 8a15d188256..54bf4621c4d 100644 --- a/micro-ui/web/package.json +++ b/micro-ui/web/package.json @@ -26,7 +26,9 @@ "packages/components", "packages/svg-components", "packages/microplan", - "packages/campaign" + "packages/campaign", + "packages/hrms", + "packages/ui" ], "scripts": { "build": "yarn lerna run build", @@ -43,4 +45,4 @@ "dependencies": { "dotenv": "^16.3.1" } -} \ No newline at end of file +} diff --git a/micro-ui/web/packages/ui-core/src/components/TopBarSideBar/index.js b/micro-ui/web/packages/ui-core/src/components/TopBarSideBar/index.js index b441ba09101..cfd23e88a32 100644 --- a/micro-ui/web/packages/ui-core/src/components/TopBarSideBar/index.js +++ b/micro-ui/web/packages/ui-core/src/components/TopBarSideBar/index.js @@ -41,7 +41,7 @@ const TopBarSideBar = ({ ]; return ( - ); }; -export default TopBarSideBar; \ No newline at end of file +export default TopBarSideBar; diff --git a/micro-ui/web/packages/ui/.babelrc b/micro-ui/web/packages/ui/.babelrc new file mode 100644 index 00000000000..eb7f5951a9c --- /dev/null +++ b/micro-ui/web/packages/ui/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + "@babel/preset-env","@babel/preset-react" + ], + "plugins": ["react-html-attrs"] + } \ No newline at end of file diff --git a/micro-ui/web/packages/ui/.gitattributes b/micro-ui/web/packages/ui/.gitattributes new file mode 100644 index 00000000000..dfe0770424b --- /dev/null +++ b/micro-ui/web/packages/ui/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/micro-ui/web/packages/ui/.gitignore b/micro-ui/web/packages/ui/.gitignore new file mode 100644 index 00000000000..9b889191a39 --- /dev/null +++ b/micro-ui/web/packages/ui/.gitignore @@ -0,0 +1,63 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# Serverless directories +.serverless/ \ No newline at end of file diff --git a/micro-ui/web/packages/ui/LICENSE b/micro-ui/web/packages/ui/LICENSE new file mode 100644 index 00000000000..1e80e19158f --- /dev/null +++ b/micro-ui/web/packages/ui/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 design-egov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/micro-ui/web/packages/ui/README.md b/micro-ui/web/packages/ui/README.md new file mode 100644 index 00000000000..4c5647143bf --- /dev/null +++ b/micro-ui/web/packages/ui/README.md @@ -0,0 +1 @@ +# core \ No newline at end of file diff --git a/micro-ui/web/packages/ui/docker/Dockerfile b/micro-ui/web/packages/ui/docker/Dockerfile new file mode 100644 index 00000000000..9fb65ec5b5e --- /dev/null +++ b/micro-ui/web/packages/ui/docker/Dockerfile @@ -0,0 +1,47 @@ +# Use Node.js base image with version 16 +FROM node:16 AS build + +# Set working directory +WORKDIR /app + +# Set build arguments +ARG BRANCH_NAME +ARG ACTION_NUMBER +ARG COMMIT_ID + +# Set environment variables based on build arguments +ENV BRANCH_NAME=$BRANCH_NAME +ENV ACTION_NUMBER=$ACTION_NUMBER +ENV COMMIT_ID=$COMMIT_ID + +# Copy package.json and yarn.lock (if exists) +COPY package.json ./ + +RUN rm -rf node_modules/ + +RUN rm -rf yarn.lock + +# clear cache +RUN yarn cache clean + +# Install dependencies +RUN yarn install + +# Optionally, you can add a label with the commit ID +LABEL commit_id=$COMMIT_ID + +# Copy the rest of the application +COPY . . + +# Build the React app with Webpack +RUN yarn build + +FROM nginx:mainline-alpine + +ENV WORK_DIR=/var/web/ui-core-mfe + +RUN mkdir -p ${WORK_DIR} + + +COPY --from=build /app/dist ${WORK_DIR}/ +COPY --from=build /app/docker/nginx.conf /etc/nginx/conf.d/default.conf diff --git a/micro-ui/web/packages/ui/docker/nginx.conf b/micro-ui/web/packages/ui/docker/nginx.conf new file mode 100644 index 00000000000..ab0d10f7905 --- /dev/null +++ b/micro-ui/web/packages/ui/docker/nginx.conf @@ -0,0 +1,12 @@ +server +{ + listen 80; + underscores_in_headers on; + + location /ui-core-mfe + { + root /var/web; + index index.html index.htm; + try_files $uri $uri/ /ui-core-mfe/index.html; + } +} \ No newline at end of file diff --git a/micro-ui/web/packages/ui/package.json b/micro-ui/web/packages/ui/package.json new file mode 100644 index 00000000000..b5b390dea85 --- /dev/null +++ b/micro-ui/web/packages/ui/package.json @@ -0,0 +1,64 @@ +{ + "name": "ui", + "version": "1.0.2", + "description": "", + "main": "index.js", + "private": true, + "scripts": { + "start": "webpack serve --config webpack.dev.js", + "build": "webpack --config webpack.prod.js", + "build:dev": "webpack --config webpack.dev.js", + "predeploy": "yarn build", + "deploy": "gh-pages -d dist" + }, + "homepage": "http://design-egov.github.io/core", + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/core": "7.12.10", + "@babel/preset-env": "7.23.9", + "@babel/preset-react": "7.12.10", + "autoprefixer": "^10.4.13", + "babel-loader": "8.2.2", + "babel-plugin-react-html-attrs": "^3.0.5", + + + "react-query-devtools": "^2.6.3", + "regex-escape": "^3.4.10", + + + "webpack": "^5.75.0", + "webpack-cli": "^5.0.1", + "webpack-dev-server": "^4.11.1", + "webpack-merge": "5.7.3" + }, + "dependencies": { + "@digit-ui/digit-ui-components": "1.0.3", + "@digit-ui/digit-ui-libraries-mfe": "1.0.17", + "@digit-ui/digit-ui-react-components": "1.0.1", + + "@rjsf/core": "5.10.0", + "@rjsf/utils": "5.10.0", + "@rjsf/validator-ajv8": "5.10.0", + + "i18next": "19.9.2", + "i18next-browser-languagedetector": "^7.2.1", + "i18next-http-backend": "^2.5.1", + "i18next-react-postprocessor": "3.0.7", + "lodash.merge": "^4.6.2", + "pdfmake": "0.1.72", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-i18next": "11.16.2", + "react-query": "3.6.1", + "react-redux": "7.2.8", + "react-router-dom": "5.3.0", + "react-tooltip": "^5.21.1", + "redux": "4.1.2", + "redux-thunk": "^2.4.2", + "rxjs": "6.6.3", + "single-spa": "^5.9.3", + "single-spa-react": "^4.6.1" + } +} diff --git a/micro-ui/web/packages/ui/public/index.html b/micro-ui/web/packages/ui/public/index.html new file mode 100644 index 00000000000..e13912b19bf --- /dev/null +++ b/micro-ui/web/packages/ui/public/index.html @@ -0,0 +1,126 @@ + + + + + Core Page + + + + + + + + + + + + + + + + +
+ + + diff --git a/micro-ui/web/packages/ui/src/App.js b/micro-ui/web/packages/ui/src/App.js new file mode 100644 index 00000000000..e2d89001641 --- /dev/null +++ b/micro-ui/web/packages/ui/src/App.js @@ -0,0 +1,81 @@ +import React, { lazy, Suspense,useEffect } from "react"; +import { Switch, Route } from "react-router-dom"; +import { DigitUI } from "./Module"; +import { initLibraries } from "@digit-ui/digit-ui-libraries-mfe"; +import registerRemotes from "./utils/registerRemotes" +import { useTranslation } from "react-i18next"; +// import { queryClient } from "./bootstrap"; +import { Loader } from "@digit-ui/digit-ui-react-components"; + +const initDigitUI = () => { + // window.contextPath = window?.globalConfigs?.getConfig("CONTEXT_PATH") || "core-digit-ui"; + window.contextPath = "ui"; + + window.Digit.Customizations = { + + }; + window?.Digit.ComponentRegistryService.setupRegistry({ + // PaymentModule, + // ...paymentConfigs, + // PaymentLinks, + }); + + // initHRMSComponents(); + const enabledModules=["PT"]; + + const moduleReducers = (initData) => initData; + + const stateCode = window?.globalConfigs?.getConfig("STATE_LEVEL_TENANT_ID") || "pb"; + // initTokens(stateCode); + + // return (); +}; + +initLibraries() +initDigitUI() + + + +const App = ({queryClient}) => { + const {t,i18n} = useTranslation() + const enabledModules=["PT","HRMS","Workbench","DSS","Measurement","PGR","Engagement","Campaign"] + const moduleReducers = (initData) => initData; + const { isLoading } = Digit.Hooks.core.useLocalization({ + params:{ + tenantId: Digit.ULBService.getStateId(), + module: `rainmaker-common,rainmaker-${Digit.ULBService.getCurrentTenantId()},rainmaker-${Digit.ULBService.getStateId()}`, + locale:i18n.language, + }, + i18n, + }) + + + + const stateCode = window?.globalConfigs?.getConfig("STATE_LEVEL_TENANT_ID") || "pb"; + + if(isLoading){ + return + } + + return ( + }> + + { +
+ +
+ }
+
+
+ + ); +}; + + + + + + + +export default App; + diff --git a/micro-ui/web/packages/ui/src/Module.js b/micro-ui/web/packages/ui/src/Module.js new file mode 100644 index 00000000000..c445891c029 --- /dev/null +++ b/micro-ui/web/packages/ui/src/Module.js @@ -0,0 +1,113 @@ +import { Body, Loader } from "@digit-ui/digit-ui-react-components"; +import React from "react"; +import { getI18n } from "react-i18next"; +import { QueryClient, QueryClientProvider } from "react-query"; +import { Provider } from "react-redux"; +import { BrowserRouter as Router } from "react-router-dom"; +import { DigitApp } from "./pages/index"; +import SelectOtp from "./pages/citizen/Login/SelectOtp"; +import { useState } from "react"; +import ErrorBoundary from "./components/ErrorBoundaries"; +import getStore from "./redux/store"; +//here add react-query dev tools +// import { ReactQueryDevtools } from 'react-query/devtools'; + +const DigitUIWrapper = ({ stateCode="pg", enabledModules, moduleReducers,defaultLanding,queryClient }) => { + + const { isLoading, data: initData } = Digit.Hooks.useInitStore(stateCode, enabledModules); + // const reduxRsp = getStore(initData, moduleReducers(initData)) + + if (isLoading) { + return ; + } + // const i18n = getI18n(); + return ( + + + + + + + + ); +}; + +export const DigitUI = ({stateCode="pg", registry, enabledModules, moduleReducers ,defaultLanding,queryClient}) => { + + const [privacy, setPrivacy] = useState(Digit.Utils.getPrivacyObject() || {}); + const ComponentProvider = Digit.Contexts.ComponentProvider; + const PrivacyProvider = Digit.Contexts.PrivacyProvider; + + + return ( +
+ + {/* */} + + { + Digit.Utils.setPrivacyObject({}); + setPrivacy({}); + }, + getPrivacy: () => { + const privacyObj = Digit.Utils.getPrivacyObject(); + setPrivacy(privacyObj); + return privacyObj; + }, + /* Descoped method to update privacy object */ + updatePrivacyDescoped: (_data) => { + const privacyObj = Digit.Utils.getAllPrivacyObject(); + const newObj = { ...privacyObj, [window.location.pathname]: _data }; + Digit.Utils.setPrivacyObject({ ...newObj }); + setPrivacy(privacyObj?.[window.location.pathname] || {}); + }, + /** + * Main Method to update the privacy object anywhere in the application + * + * @author jagankumar-egov + * + * Feature :: Privacy + * + * @example + * const { privacy , updatePrivacy } = Digit.Hooks.usePrivacyContext(); + */ + updatePrivacy: (uuid, fieldName) => { + setPrivacy(Digit.Utils.updatePrivacy(uuid, fieldName) || {}); + }, + }} + > + + + {/*
Core Module Dummy
*/} + + {/* */} +
+
+ {/*
*/} +
+
+ ); + +}; + +const componentsToRegister = { + SelectOtp, +}; + + +export const initCoreComponents = () => { + + Object.entries(componentsToRegister).forEach(([key, value]) => { + Digit.ComponentRegistryService.setComponent(key, value); + }); +}; diff --git a/micro-ui/web/packages/ui/src/bootstrap.js b/micro-ui/web/packages/ui/src/bootstrap.js new file mode 100644 index 00000000000..14c1e61426c --- /dev/null +++ b/micro-ui/web/packages/ui/src/bootstrap.js @@ -0,0 +1,81 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; +import { HashRouter } from 'react-router-dom'; +import '../src/locale/i18n'; +import { CustomisedHooks } from './hooks'; +import { QueryClient,QueryClientProvider } from 'react-query'; +import registerRemotes from './utils/registerRemotes'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 15 * 60 * 1000, + cacheTime: 50 * 60 * 1000, + retry: false, + retryDelay: (attemptIndex) => Infinity, + /* + enable this to have auto retry incase of failure + retryDelay: attemptIndex => Math.min(1000 * 3 ** attemptIndex, 60000) + */ + }, + }, +}); + + +registerRemotes(queryClient) + +/* To Overide any existing hook we need to use similar method */ +const setupHooks = (HookName, HookFunction, method, isHook = true) => { + window.Digit = window.Digit || {}; + + window.Digit[isHook ? 'Hooks' : 'Utils'] = + window.Digit[isHook ? 'Hooks' : 'Utils'] || {}; + window.Digit[isHook ? 'Hooks' : 'Utils'][HookName] = + window.Digit[isHook ? 'Hooks' : 'Utils'][HookName] || {}; + window.Digit[isHook ? 'Hooks' : 'Utils'][HookName][HookFunction] = method; +}; + +/* To Overide any existing libraries we need to use similar method */ +const setupLibraries = (Library, service, method) => { + window.Digit = window.Digit || {}; + window.Digit[Library] = window.Digit[Library] || {}; + window.Digit[Library][service] = method; +}; + +const overrideHooks = () => { + Object.keys(CustomisedHooks).map((ele) => { + if (ele === 'Hooks') { + Object.keys(CustomisedHooks[ele]).map((hook) => { + Object.keys(CustomisedHooks[ele][hook]).map((method) => { + setupHooks(hook, method, CustomisedHooks[ele][hook][method]); + }); + }); + } else if (ele === 'Utils') { + Object.keys(CustomisedHooks[ele]).map((hook) => { + Object.keys(CustomisedHooks[ele][hook]).map((method) => { + setupHooks(hook, method, CustomisedHooks[ele][hook][method], false); + }); + }); + } else { + Object.keys(CustomisedHooks[ele]).map((method) => { + setupLibraries(ele, method, CustomisedHooks[ele][method]); + }); + } + }); +}; + +overrideHooks(); + +const AppWithRouter = () => { + + return ( + + + + + + ); +}; + +ReactDOM.render(, document.querySelector('#root')); diff --git a/micro-ui/web/packages/ui/src/components/AppModules.js b/micro-ui/web/packages/ui/src/components/AppModules.js new file mode 100644 index 00000000000..f326702dee7 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/AppModules.js @@ -0,0 +1,54 @@ +import React from "react"; +import { Redirect, Route, Switch, useLocation, useRouteMatch } from "react-router-dom"; + +import ChangePassword from "../pages/employee/ChangePassword/index"; +import ForgotPassword from "../pages/employee/ForgotPassword/index"; +import { AppHome } from "./Home"; +// import UserProfile from "./userProfile"; + +const getTenants = (codes, tenants) => { + return tenants.filter((tenant) => codes?.map?.((item) => item.code).includes(tenant.code)); +}; + +export const AppModules = ({ stateCode="pg", userType, modules, appTenants }) => { + + const ComponentProvider = Digit.Contexts.ComponentProvider; + const { path } = useRouteMatch(); + const location = useLocation(); + + const user = Digit.UserService.getUser(); + + if (!user || !user?.access_token || !user?.info) { + return ; + } + + const appRoutes = modules.map(({ code, tenants }, index) => { + const Module = Digit.ComponentRegistryService.getComponent(`${code}Module`); + return Module ? ( + + + + ) : null + }); + + return ( +
+ + {appRoutes} + + + + + + + + + + + + + {/* */} + +
+ ); +}; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/Background.js b/micro-ui/web/packages/ui/src/components/Background.js new file mode 100644 index 00000000000..b2838576df9 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Background.js @@ -0,0 +1,7 @@ +import React from "react"; + +const Background = ({ children }) => { + return
{children}
; +}; + +export default Background; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/ChangeCity.js b/micro-ui/web/packages/ui/src/components/ChangeCity.js new file mode 100644 index 00000000000..8384acb1ba5 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/ChangeCity.js @@ -0,0 +1,92 @@ +import { Dropdown } from "@digit-ui/digit-ui-react-components"; +import React, { useEffect, useState } from "react"; +import { useHistory } from "react-router-dom"; + +const stringReplaceAll = (str = "", searcher = "", replaceWith = "") => { + if (searcher == "") return str; + while (str?.includes(searcher)) { + str = str?.replace(searcher, replaceWith); + } + return str; +}; + +const ChangeCity = (prop) => { + const [dropDownData, setDropDownData] = useState(null); + const [selectCityData, setSelectCityData] = useState([]); + const [selectedCity, setSelectedCity] = useState([]); //selectedCities?.[0]?.value + const history = useHistory(); + const isDropdown = prop.dropdown || false; + let selectedCities = []; + + const handleChangeCity = (city) => { + const loggedInData = Digit.SessionStorage.get("citizen.userRequestObject"); + const filteredRoles = Digit.SessionStorage.get("citizen.userRequestObject")?.info?.roles?.filter((role) => role.tenantId === city.value); + if (filteredRoles?.length > 0) { + loggedInData.info.roles = filteredRoles; + loggedInData.info.tenantId = city?.value; + } + Digit.SessionStorage.set("Employee.tenantId", city?.value); + Digit.UserService.setUser(loggedInData); + setDropDownData(city); + if (window.location.href.includes(`/${window?.contextPath}/employee/`)) { + const redirectPath = location.state?.from || `/${window?.contextPath}/employee`; + history.replace(redirectPath); + } + window.location.reload(); + }; + + useEffect(() => { + const userloggedValues = Digit.SessionStorage.get("citizen.userRequestObject"); + let teantsArray = [], + filteredArray = []; + userloggedValues?.info?.roles?.forEach((role) => teantsArray.push(role.tenantId)); + let unique = teantsArray.filter((item, i, ar) => ar.indexOf(item) === i); + unique?.forEach((uniCode) => { + filteredArray.push({ + label: `TENANT_TENANTS_${stringReplaceAll(uniCode, ".", "_")?.toUpperCase()}`, + value: uniCode, + }); + }); + selectedCities = filteredArray?.filter((select) => select.value == Digit.SessionStorage.get("Employee.tenantId")); + setSelectCityData(filteredArray); + }, [dropDownData]); + + // if (isDropdown) { + return ( +
+ cityValue.value === dropDownData?.value)} + optionKey={"label"} + select={handleChangeCity} + freeze={true} + customSelector={ + + } + /> +
+ ); + // } else { + // return ( + // + //
City
+ //
+ // {selectCityData?.map((city, index) => ( + //
+ // handleChangeCity(city)} + // > + //
+ // ))} + //
+ //
+ // ); + // } +}; + +export default ChangeCity; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/ChangeLanguage.js b/micro-ui/web/packages/ui/src/components/ChangeLanguage.js new file mode 100644 index 00000000000..90b511678da --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/ChangeLanguage.js @@ -0,0 +1,53 @@ +import { CustomButton, Dropdown } from "@digit-ui/digit-ui-react-components"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; + +const ChangeLanguage = (prop) => { + const isDropdown = prop.dropdown || false; + const { data: storeData, isLoading } = Digit.Hooks.useStore.getInitData(); + const { languages, stateInfo } = storeData || {}; + const selectedLanguage = Digit.StoreData.getCurrentLanguage(); + const [selected, setselected] = useState(selectedLanguage); + const { i18n } = useTranslation(); + const handleChangeLanguage = (language) => { + setselected(language.value); + i18n.changeLanguage(language.value); + Digit.LocalizationService.changeLanguage(language.value, stateInfo.code); + }; + + if (isLoading) return null; + + if (isDropdown) { + return ( +
+ language.value === selectedLanguage)} + optionKey={"label"} + select={handleChangeLanguage} + freeze={true} + customSelector={} + /> +
+ ); + } else { + return ( + +
Language
+
+ {languages.map((language, index) => ( +
+ handleChangeLanguage(language)} + > +
+ ))} +
+
+ ); + } +}; + +export default ChangeLanguage; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/Dialog/LogoutDialog.js b/micro-ui/web/packages/ui/src/components/Dialog/LogoutDialog.js new file mode 100644 index 00000000000..f7dde8dde9f --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Dialog/LogoutDialog.js @@ -0,0 +1,114 @@ +import { CardText, CloseSvg, Modal } from "@digit-ui/digit-ui-react-components"; +import React from "react"; +import { useTranslation } from "react-i18next"; + +const Heading = (props) => { + return

{props.label}

; +}; +const Close = () => ( + + + + +); +const CloseBtn = (props) => { + return ( +
+ {props?.isMobileView ? ( + + ) : ( +
+ {" "} + {" "} +
+ )} +
+ ); +}; +const LogoutDialog = ({ onSelect, onCancel, onDismiss }) => { + const { t } = useTranslation(); + const mobileDeviceWidth = 780; + const [isMobileView, setIsMobileView] = React.useState(window.innerWidth <= mobileDeviceWidth); + const onResize = () => { + if (window.innerWidth <= mobileDeviceWidth) { + if (!isMobileView) { + setIsMobileView(true); + } + } else { + if (isMobileView) { + setIsMobileView(false); + } + } + }; + React.useEffect(() => { + window.addEventListener("resize", () => { + onResize(); + }); + return () => { + window.addEventListener("resize", () => { + onResize(); + }); + }; + }); + return isMobileView ? ( + } + headerBarEnd={} + actionCancelLabel={t("CORE_LOGOUT_CANCEL")} + actionCancelOnSubmit={onCancel} + actionSaveLabel={t("CORE_LOGOUT_WEB_YES")} + actionSaveOnSubmit={onSelect} + formId="modal-action" + > +
+ {t("CORE_LOGOUT_WEB_CONFIRMATION_MESSAGE") + " "} +
+
+ ) : ( + } + headerBarEnd={} + actionCancelLabel={t("CORE_LOGOUT_CANCEL")} + actionCancelOnSubmit={onCancel} + actionSaveLabel={t("CORE_LOGOUT_WEB_YES")} + actionSaveOnSubmit={onSelect} + formId="modal-action" + > +
+ + {t("CORE_LOGOUT_WEB_CONFIRMATION_MESSAGE") + " "} + {t("CORE_LOGOUT_MESSAGE")}? + +
+
+ ); +}; +export default LogoutDialog; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/ErrorBoundaries.js b/micro-ui/web/packages/ui/src/components/ErrorBoundaries.js new file mode 100644 index 00000000000..ac40f2af7db --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/ErrorBoundaries.js @@ -0,0 +1,57 @@ +import React from "react"; +import ErrorComponent from "./ErrorComponent"; + +const Redircter = () => { + const path = Digit.UserService.getType() === "employee" ? `/${window?.contextPath}/employee/user/error` : `/${window?.contextPath}/citizen/error`; + if ( + window.location.href.includes("employee/user/error") || + window.location.href.includes("citizen/error") || + process.env.NODE_ENV === "development" + ) { + //do nothing + }else{ + window.location.href = path; + } + return ; +}; + +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { error: null, errorStack: null, hasError: false }; + } + + static getDerivedStateFromError(error) { + // Update state so the next render will show the fallback UI. + return { error: error?.message, hasError: true, errorStack: error?.stack }; + } + + componentDidCatch(error, errorInfo) { + // Catch errors in any components below and re-render with error message + this.setState({ error: error?.message, hasError: true, errorStack: error?.stack }); + // You can also log error messages to an error reporting service here + } + + render() { + if (this.state.hasError) { + // ("UI-errorInfo", this.state?.errorStack); + // ("UI-component-details", this.props); + // You can render any custom fallback UI + return ( +
+ + + + {/* Something went wrong +
+ {this.state?.errorStack && this.state.errorStack.toString().substring(0, 600)} + {this.state?.error} +
*/} +
+ ); + } + return this.props.children; + } +} + +export default ErrorBoundary; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/ErrorComponent.js b/micro-ui/web/packages/ui/src/components/ErrorComponent.js new file mode 100644 index 00000000000..f10ba4a823e --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/ErrorComponent.js @@ -0,0 +1,46 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +const ErrorConfig = { + error: { + imgUrl: `https://s3.ap-south-1.amazonaws.com/egov-qa-assets/error-image.png`, + infoMessage: "CORE_SOMETHING_WENT_WRONG", + buttonInfo: "ACTION_TEST_HOME", + }, + maintenance: { + imgUrl: `https://s3.ap-south-1.amazonaws.com/egov-qa-assets/maintainence-image.png`, + infoMessage: "CORE_UNDER_MAINTENANCE", + buttonInfo: "ACTION_TEST_HOME", + }, + notfound: { + imgUrl: `https://s3.ap-south-1.amazonaws.com/egov-qa-assets/PageNotFound.png`, + infoMessage: "MODULE_NOT_FOUND", + buttonInfo: "ACTION_TEST_HOME", + }, +}; + +const ErrorComponent = (props) => { + const { type = "error" } = Digit.Hooks.useQueryParams(); + const config = ErrorConfig[type]; + const { t } = useTranslation(); + + const stateInfo = props.stateInfo; + + return ( +
+
+ error +

{t(config.infoMessage)}

+ +
+
+ ); +}; + +export default ErrorComponent; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/Header.js b/micro-ui/web/packages/ui/src/components/Header.js new file mode 100644 index 00000000000..befc1acfe0e --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Header.js @@ -0,0 +1,20 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Loader } from "@digit-ui/digit-ui-react-components" + +const Header = () => { + const { data: storeData, isLoading } = Digit.Hooks.useStore.getInitData(); + const { stateInfo } = storeData || {}; + const { t } = useTranslation() + + if (isLoading) return ; + + return ( +
+ +

{t(`TENANT_TENANTS_${stateInfo?.code.toUpperCase()}`)}

+
+ ); +} + +export default Header; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/Home.js b/micro-ui/web/packages/ui/src/components/Home.js new file mode 100644 index 00000000000..00cd5fa332e --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Home.js @@ -0,0 +1,147 @@ +import { + BackButton, + BillsIcon, + CitizenHomeCard, + CitizenInfoLabel, + FSMIcon, + Loader, + MCollectIcon, + OBPSIcon, + PGRIcon, + PTIcon, + TLIcon, + WSICon, + } from "@digit-ui/digit-ui-react-components"; + import React from "react"; + import { useTranslation } from "react-i18next"; + + /* + Feature :: Citizen All service screen cards + */ + export const processLinkData = (newData, code, t) => { + const obj = newData?.[`${code}`]; + if (obj) { + obj.map((link) => { + (link.link = link["navigationURL"]), (link.i18nKey = t(link["name"])); + }); + } + const newObj = { + links: obj?.reverse(), + header: Digit.Utils.locale.getTransformedLocale(`ACTION_TEST_${code}`), + iconName: `CITIZEN_${code}_ICON`, + }; + if (code === "FSM") { + const roleBasedLoginRoutes = [ + { + role: "FSM_DSO", + from: `/${window?.contextPath}/citizen/fsm/dso-dashboard`, + dashoardLink: "CS_LINK_DSO_DASHBOARD", + loginLink: "CS_LINK_LOGIN_DSO", + }, + ]; + //RAIN-7297 + roleBasedLoginRoutes.map(({ role, from, loginLink, dashoardLink }) => { + if (Digit.UserService.hasAccess(role)) + newObj?.links?.push({ + link: from, + i18nKey: t(dashoardLink), + }); + else + newObj?.links?.push({ + link: `/${window?.contextPath}/citizen/login`, + state: { role: "FSM_DSO", from }, + i18nKey: t(loginLink), + }); + }); + } + + return newObj; + }; + const iconSelector = (code) => { + switch (code) { + case "PT": + return ; + case "WS": + return ; + case "FSM": + return ; + case "MCollect": + return ; + case "PGR": + return ; + case "TL": + return ; + case "OBPS": + return ; + case "Bills": + return ; + default: + return ; + } + }; + const CitizenHome = ({ modules, getCitizenMenu, fetchedCitizen, isLoading }) => { + const paymentModule = modules.filter(({ code }) => code === "Payment")[0]; + const moduleArr = modules.filter(({ code }) => code !== "Payment"); + const moduleArray = [paymentModule, ...moduleArr]; + const { t } = useTranslation(); + if (isLoading) { + return ; + } + + return ( + +
+ {location.pathname.includes("sanitation-ui/citizen/all-services") ? null : } +
+ {moduleArray + .filter((mod) => mod) + .map(({ code }, index) => { + let mdmsDataObj; + if (fetchedCitizen) mdmsDataObj = fetchedCitizen ? processLinkData(getCitizenMenu, code, t) : undefined; + if (mdmsDataObj?.links?.length > 0) { + return ( + ele?.link)?.sort((x, y) => x?.orderNumber - y?.orderNumber)} + Icon={() => iconSelector(code)} + Info={ + code === "OBPS" + ? () => ( + + ) + : null + } + isInfo={code === "OBPS" ? true : false} + /> + ); + } else return ; + })} +
+
+
+ ); + }; + + const EmployeeHome = ({ modules }) => { + return ( +
+
+ {modules.map(({ code }, index) => { + const Card = Digit.ComponentRegistryService.getComponent(`${code}Card`) || (() => ); + return ; + })} +
+
+ ); + }; + + export const AppHome = ({ userType, modules, getCitizenMenu, fetchedCitizen, isLoading }) => { + if (userType === "citizen") { + return ; + } + return ; +}; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/Loader.js b/micro-ui/web/packages/ui/src/components/Loader.js new file mode 100644 index 00000000000..3d3f5539a2d --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Loader.js @@ -0,0 +1,11 @@ +import React from "react"; + +const Loader = () => { + return ( +
+
+
+ ); +}; + +export default Loader; diff --git a/micro-ui/web/packages/ui/src/components/Search/MobileSearchApplication.js b/micro-ui/web/packages/ui/src/components/Search/MobileSearchApplication.js new file mode 100644 index 00000000000..17fa97fd12a --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Search/MobileSearchApplication.js @@ -0,0 +1,174 @@ +import { + BackButton, CloseSvg, DetailsCard, DownloadBtnCommon, Header, Loader, PopUp, SearchAction, SearchForm + } from "@digit-ui/digit-ui-react-components"; + import React, { useCallback, useEffect, useMemo, useReducer, useState } from "react"; + import SearchFormFields from "./SearchFields"; + // import { convertEpochToDateDMY } from "../../utils"; + + const MobileSearchApplication = ({ Controller, register, control, t, reset, previousPage, handleSubmit, tenantId, data, onSubmit, isLoading }) => { + function activateModal(state, action) { + switch (action.type) { + case "set": + return action.payload; + case "remove": + return false; + default: + break; + } + } + const [tabledata, settabledata] = useState([]); + const DownloadBtn = (props) => { + return ( +
+ +
+ ); + }; + const handleExcelDownload = (tabData) => { + if (tabData?.[0] !== undefined) { + return Digit.Download.Excel(tabData?.[0], "AuditReport"); + } + }; + useEffect(() => { + if (data?.length > 0) { + settabledata([ + data?.map((obj) => { + let returnObject = {}; + returnObject[t("AUDIT_DATE_LABEL")] = convertEpochToDate(obj?.timestamp); + returnObject[t("AUDIT_TIME_LABEL")] = convertEpochToTimeInHours(obj?.timestamp); + returnObject[t("AUDIT_DATAVIEWED_LABEL")] = obj?.dataView[0] + "," + obj?.dataView[1]; + returnObject[t("AUDIT_DATAVIEWED_BY_LABEL")] = obj?.dataViewedBy; + returnObject[t("AUDIT_ROLE_LABEL")] = obj?.roles.map((obj) => obj.name).join(","); + return { + ...returnObject, + }; + }), + ]); + } + }, [data]); + const convertEpochToDate = (dateEpoch) => { + if (dateEpoch == null || dateEpoch == undefined || dateEpoch == "") { + return "NA"; + } + const dateFromApi = new Date(dateEpoch); + let month = dateFromApi.getMonth() + 1; + let day = dateFromApi.getDate(); + let year = dateFromApi.getFullYear(); + month = (month > 9 ? "" : "0") + month; + day = (day > 9 ? "" : "0") + day; + return `${day}/${month}/${year}`; + }; + const convertEpochToTimeInHours = (dateEpoch) => { + if (dateEpoch == null || dateEpoch == undefined || dateEpoch == "") { + return "NA"; + } + const dateFromApi = new Date(dateEpoch); + let hour = dateFromApi.getHours(); + let min = dateFromApi.getMinutes(); + let period = hour > 12 ? "PM" : "AM"; + hour = hour > 12 ? hour - 12 : hour; + hour = (hour > 9 ? "" : "0") + hour; + min = (min > 9 ? "" : "0") + min; + return `${hour}:${min} ${period}`; + }; + const [currentlyActiveMobileModal, setActiveMobileModal] = useReducer(activateModal, false); + + const closeMobilePopupModal = () => { + setActiveMobileModal({ type: "remove" }); + }; + + const MobilePopUpCloseButton = () => ( +
+ +
+ ); + const searchFormFieldsComponentProps = { Controller, register, control, t, reset, previousPage }; + + const MobileComponentDirectory = ({ currentlyActiveMobileModal, searchFormFieldsComponentProps, tenantId, ...props }) => { + const { closeMobilePopupModal } = props; + switch (currentlyActiveMobileModal) { + case "SearchFormComponent": + return ( + + +
+

{t("PRIVACY_AUDIT_REPORT")}:

+
+ + {/* + +

{t(`ES_COMMON_CLEAR_ALL`)}

+
*/} +
+ ); + default: + return ; + } + }; + const CurrentMobileModalComponent = useCallback( + ({ currentlyActiveMobileModal, searchFormFieldsComponentProps, tenantId, ...props }) => + MobileComponentDirectory({ currentlyActiveMobileModal, searchFormFieldsComponentProps, tenantId, ...props }), + [currentlyActiveMobileModal] + ); + let roles = []; + data?.roles?.forEach((item) => { + roles.push(item?.name); + }); + const propsMobileInboxCards = useMemo(() => { + if (data?.display) { + return []; + } + if (data === "") { + return []; + } + return data?.map((data) => ({ + [t("AUDIT_DATE_LABEL")]: convertEpochToDate(data.timestamp), + [t("AUDIT_TIME_LABEL")]: convertEpochToTimeInHours(data.timestamp), + [t("AUDIT_DATAVIEWED_LABEL")]: data.dataView[0] + "," + data.dataView[1], + [t("AUDIT_DATAVIEWED_BY_LABEL")]: data.dataViewedBy, + [t("AUDIT_ROLE_LABEL")]: data.roles + .slice(0, 3) + ?.map((e) => e.name) + .join(","), + })); + }, [data]); + + return ( + + +
+ handleExcelDownload(tabledata)} /> +
+
{t("PRIVACY_AUDIT_REPORT")}:
+
+ setActiveMobileModal({ type: "set", payload: "SearchFormComponent" })} + {...{ tenantId, t }} + /> +
+ {currentlyActiveMobileModal ? ( + + { + setActiveMobileModal({ type: "remove" }); + onSubmit(data); + }} + handleSubmit={handleSubmit} + id="search-form" + className="rm-mb form-field-flex-one inboxPopupMobileWrapper" + {...{ searchFormFieldsComponentProps, currentlyActiveMobileModal, closeMobilePopupModal, tenantId }} + /> + + ) : null} + {isLoading && } + +
+ ); + }; + + export default MobileSearchApplication; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/Search/SearchFields.js b/micro-ui/web/packages/ui/src/components/Search/SearchFields.js new file mode 100644 index 00000000000..0b37f10a119 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Search/SearchFields.js @@ -0,0 +1,29 @@ +import { + DatePicker, SearchField, SubmitBar +} from "@digit-ui/digit-ui-react-components"; +import React from "react"; +import { Controller } from "react-hook-form"; +const SearchFields = ({ register, control, reset, tenantId, t, previousPage, formState, isLoading }) => { + const isMobile = window.Digit.Utils.browser.isMobile(); + + return ( + <> + + + } name="fromDate" control={control} /> + + + + } name="toDate" control={control} /> + + + + + + ); +}; +export default SearchFields; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/Search/index.js b/micro-ui/web/packages/ui/src/components/Search/index.js new file mode 100644 index 00000000000..e239f96ba8d --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/Search/index.js @@ -0,0 +1,208 @@ +import { BackButton, DownloadBtnCommon, Header, Loader, SearchForm, Table } from "@digit-ui/digit-ui-react-components"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { Controller, useForm } from "react-hook-form"; +import MobileSearchApplication from "./MobileSearchApplication"; +import SearchFields from "./SearchFields"; + +const SearchApplication = ({ tenantId, t, onSubmit, data, count }) => { + const initialValues = Digit.SessionStorage.get("AUDIT_APPLICATION_DETAIL") || { + offset: 0, + limit: 5, + sortOrder: "DESC", + }; + const { register, control, handleSubmit, setValue, getValues, reset } = useForm({ + defaultValues: initialValues, + }); + const convertEpochToDate = (dateEpoch) => { + if (dateEpoch == null || dateEpoch == undefined || dateEpoch == "") { + return "NA"; + } + const dateFromApi = new Date(dateEpoch); + let month = dateFromApi.getMonth() + 1; + let day = dateFromApi.getDate(); + let year = dateFromApi.getFullYear(); + month = (month > 9 ? "" : "0") + month; + day = (day > 9 ? "" : "0") + day; + return `${day}/${month}/${year}`; + }; + const convertEpochToTimeInHours = (dateEpoch) => { + if (dateEpoch == null || dateEpoch == undefined || dateEpoch == "") { + return "NA"; + } + const dateFromApi = new Date(dateEpoch); + let hour = dateFromApi.getHours(); + let min = dateFromApi.getMinutes(); + let period = hour > 12 ? "PM" : "AM"; + hour = hour > 12 ? hour - 12 : hour; + hour = (hour > 9 ? "" : "0") + hour; + min = (min > 9 ? "" : "0") + min; + return `${hour}:${min} ${period}`; + }; + const [tabledata, settabledata] = useState([]); + const DownloadBtn = (props) => { + return ( +
+ +
+ ); + }; + const handleExcelDownload = (tabData) => { + if (tabData?.[0] !== undefined) { + return Digit.Download.Excel(tabData?.[0], "AuditReport"); + } + }; + useEffect(() => { + register("offset", 0); + register("limit", 5); + register("sortOrder", "DESC"); + }, [register]); + useEffect(() => { + if (data?.length > 0) { + settabledata([ + data?.map((obj) => { + let returnObject = {}; + returnObject[t("AUDIT_DATE_LABEL")] = convertEpochToDate(obj?.timestamp); + returnObject[t("AUDIT_TIME_LABEL")] = convertEpochToTimeInHours(obj?.timestamp); + returnObject[t("AUDIT_DATAVIEWED_LABEL")] = obj?.dataView[0] + "," + obj?.dataView[1]; + returnObject[t("AUDIT_DATAVIEWED_BY_LABEL")] = obj?.dataViewedBy; + returnObject[t("AUDIT_ROLE_LABEL")] = obj?.roles.map((obj) => obj.name).join(","); + return { + ...returnObject, + }; + }), + ]); + } + }, [data]); + const onSort = useCallback((args) => { + if (args.length === 0) return; + setValue("sortBy", args.id); + setValue("sortOrder", args.desc ? "DESC" : "ASC"); + }, []); + + function onPageSizeChange(e) { + setValue("limit", Number(e.target.value)); + handleSubmit(onSubmit)(); + } + + function nextPage() { + setValue("offset", getValues("offset") + getValues("limit")); + handleSubmit(onSubmit)(); + } + function previousPage() { + setValue("offset", getValues("offset") - getValues("limit")); + handleSubmit(onSubmit)(); + } + const isMobile = window.Digit.Utils.browser.isMobile(); + + if (isMobile) { + return ; + } + + //need to get from workflow + const GetCell = (value) => {value}; + const columns = useMemo( + () => [ + { + Header: t("AUDIT_DATE_LABEL"), + disableSortBy: true, + accessor: (row) => { + const timestamp = row.timestamp === "NA" ? t("WS_NA") : convertEpochToDate(row.timestamp); + return GetCell(`${timestamp}`); + }, + }, + { + Header: t("AUDIT_TIME_LABEL"), + disableSortBy: true, + accessor: (row) => { + const timestamp = row.timestamp === "NA" ? t("WS_NA") : convertEpochToTimeInHours(row.timestamp); + return GetCell(`${timestamp}`); + }, + }, + { + Header: isMobile ? t("AUDIT_DATAVIEWED_LABEL") : t("AUDIT_DATAVIEWED_PRIVACY"), + disableSortBy: true, + accessor: (row) => { + return GetCell(`${row?.dataView}`); + }, + }, + { + Header: isMobile ? t("AUDIT_DATAVIEWED_BY_LABEL") : t("AUDIT_DATAVIEWED_BY_PRIVACY"), + disableSortBy: true, + accessor: (row) => { + return GetCell(`${row?.dataViewedBy}`); + }, + }, + { + Header: t("AUDIT_ROLE_LABEL"), + disableSortBy: true, + accessor: (row) => { + return GetCell(`${row?.roles.slice(0, 3)?.map((e) => e.name)}`); + }, + }, + ], + [] + ); + + return ( + +
+ {" "} + {" "} +
+
+ {" "} +
{t("PRIVACY_AUDIT_REPORT")}
{" "} +
+ + + +
+ {data?.display ? ( +
+ {t(data.display) + ?.split("\\n") + .map((text, index) => ( +

+ {text} +

+ ))} +
+ ) : data !== "" ? ( +
+
+ handleExcelDownload(tabledata)} /> +
+ { + return { + style: { + minWidth: cellInfo.column.Header === t("ES_INBOX_APPLICATION_NO") ? "240px" : "", + padding: "20px 18px", + fontSize: "16px", + }, + }; + }} + onPageSizeChange={onPageSizeChange} + currentPage={getValues("offset") / getValues("limit")} + onNextPage={nextPage} + onPrevPage={previousPage} + manualPagination={false} + pageSizeLimit={getValues("limit")} + onSort={onSort} + disableSort={false} + sortParams={[{ id: getValues("sortBy"), desc: getValues("sortOrder") === "DESC" ? true : false }]} + /> + + ) : ( + + )} + + + ); +}; + +export default SearchApplication; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/CitizenSideBar.js b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/CitizenSideBar.js new file mode 100644 index 00000000000..ee788d545aa --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/CitizenSideBar.js @@ -0,0 +1,249 @@ +import { + Loader, NavBar + } from "@digit-ui/digit-ui-react-components"; + import React, { useState } from "react"; + import { useTranslation } from "react-i18next"; + import { useHistory } from "react-router-dom"; + import SideBarMenu from "../../../config/sidebar-menu"; + import ChangeCity from "../../ChangeCity"; + import { defaultImage } from "../../utils"; + import StaticCitizenSideBar from "./StaticCitizenSideBar"; + + + const Profile = ({ info, stateName, t }) => { + const [profilePic, setProfilePic] = React.useState(null); + React.useEffect(async () => { + const tenant = Digit.ULBService.getCurrentTenantId(); + const uuid = info?.uuid; + if (uuid) { + const usersResponse = await Digit.UserService.userSearch(tenant, { uuid: [uuid] }, {}); + + if (usersResponse && usersResponse.user && usersResponse.user.length) { + const userDetails = usersResponse.user[0]; + const thumbs = userDetails?.photo?.split(","); + setProfilePic(thumbs?.at(0)); + } + } + }, [profilePic !== null]); + return ( +
+
+ +
+
+
{info?.name}
+
+
+
{info?.mobileNumber}
+
+ {info?.emailId && ( +
+
{info.emailId}
+
+ )} +
+ {window.location.href.includes("/employee") && + !window.location.href.includes("/employee/user/login") && + !window.location.href.includes("employee/user/language-selection") && } +
+ ); + }; + const PoweredBy = () => ( +
+ Powered by DIGIT { + window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus(); + }} + />{" "} +
+ ); + + /* + Feature :: Citizen Webview sidebar + */ + export const CitizenSideBar = ({ isOpen, isMobile = false, toggleSidebar, onLogout, isEmployee = false, linkData, islinkDataLoading }) => { + const { data: storeData, isFetched } = Digit.Hooks.useStore.getInitData(); + const { stateInfo } = storeData || {}; + const user = Digit.UserService.getUser(); + const [search, setSearch] = useState(""); + + const { t } = useTranslation(); + const history = useHistory(); + const closeSidebar = () => { + Digit.clikOusideFired = true; + toggleSidebar(false); + }; + + const { isLoading, data } = Digit.Hooks.useAccessControl(); + const tenantId = Digit.ULBService.getCurrentTenantId(); + const showProfilePage = () => { + const redirectUrl = isEmployee ? `/${window?.contextPath}/employee/user/profile` : `/${window?.contextPath}/citizen/user/profile`; + history.push(redirectUrl); + closeSidebar(); + }; + const redirectToLoginPage = () => { + // localStorage.clear(); + // sessionStorage.clear(); + history.push(`/${window?.contextPath}/citizen/login`); + closeSidebar(); + }; + if (islinkDataLoading || isLoading) { + return ; + } + + let menuItems = [...SideBarMenu(t, closeSidebar, redirectToLoginPage, isEmployee)]; + let profileItem; + if (isFetched && user && user.access_token) { + profileItem = ; + menuItems = menuItems.filter((item) => item?.id !== "login-btn"); + menuItems = [ + ...menuItems, + { + text: t("EDIT_PROFILE"), + element: "PROFILE", + icon: "EditPencilIcon", + populators: { + onClick: showProfilePage, + }, + }, + { + text: t("CORE_COMMON_LOGOUT"), + element: "LOGOUT", + icon: "LogoutIcon", + populators: { + onClick: onLogout, + }, + }, + { + text: ( + + {t("CS_COMMON_HELPLINE")} +
+ {storeData?.tenants.map((i) => { + i.code === tenantId ? ( + + ) : ( + + ); + })} + +
+
+ ), + element: "Helpline", + icon: "Phone", + }, + ]; + } + + let configEmployeeSideBar = {}; + + if (!isEmployee) { + Object.keys(linkData) + ?.sort((x, y) => y.localeCompare(x)) + ?.map((key) => { + if (linkData[key][0]?.sidebar === "digit-ui-links") + menuItems.splice(1, 0, { + type: linkData[key][0]?.sidebarURL?.includes(window?.contextPath) ? "link" : "external-link", + text: t(`ACTION_TEST_${Digit.Utils.locale.getTransformedLocale(key)}`), + links: linkData[key], + icon: linkData[key][0]?.leftIcon, + link: linkData[key][0]?.sidebarURL, + }); + }); + } else { + data?.actions + .filter((e) => e.url === "url" && e.displayName !== "Home") + .forEach((item) => { + if (search == "" && item.path !== "") { + let index = item.path.split(".")[0]; + if (index === "TradeLicense") index = "Trade License"; + if (!configEmployeeSideBar[index]) { + configEmployeeSideBar[index] = [item]; + } else { + configEmployeeSideBar[index].push(item); + } + } else if (item.path !== "" && item?.displayName?.toLowerCase().includes(search.toLowerCase())) { + let index = item.path.split(".")[0]; + if (index === "TradeLicense") index = "Trade License"; + if (!configEmployeeSideBar[index]) { + configEmployeeSideBar[index] = [item]; + } else { + configEmployeeSideBar[index].push(item); + } + } + }); + const keys = Object.keys(configEmployeeSideBar); + for (let i = 0; i < keys.length; i++) { + const getSingleDisplayName = configEmployeeSideBar[keys[i]][0]?.displayName?.toUpperCase()?.replace(/[ -]/g, "_"); + const getParentDisplayName = keys[i]?.toUpperCase()?.replace(/[ -]/g, "_"); + + if (configEmployeeSideBar[keys[i]][0].path.indexOf(".") === -1) { + menuItems.splice(1, 0, { + type: "link", + text: t(`ACTION_TEST_${getSingleDisplayName}`), + link: configEmployeeSideBar[keys[i]][0]?.navigationURL, + icon: configEmployeeSideBar[keys[i]][0]?.leftIcon?.split?.(":")[1], + populators: { + onClick: () => { + history.push(configEmployeeSideBar[keys[i]][0]?.navigationURL); + closeSidebar(); + }, + }, + }); + } else { + menuItems.splice(1, 0, { + type: "dynamic", + moduleName: t(`ACTION_TEST_${getParentDisplayName}`), + links: configEmployeeSideBar[keys[i]]?.map((ob) => {return {...ob, displayName: t(`ACTION_TEST_${ob?.displayName?.toUpperCase()?.replace(/[ -]/g, "_")}`)}}), + icon: configEmployeeSideBar[keys[i]][1]?.leftIcon, + }); + } + } + const indx = menuItems.findIndex(a => a.element === "HOME"); + const home = menuItems.splice(indx,1); + const comp = menuItems.findIndex(a => a.element === "LANGUAGE"); + const part = menuItems.splice(comp,menuItems?.length-comp); + menuItems?.sort((a,b) => { + let c1 = a?.type === "dynamic" ? a?.moduleName : a?.text; + let c2 = b?.type === "dynamic" ? b?.moduleName : b?.text; + return c1.localeCompare(c2) + } ); + home?.[0] && menuItems.splice(0,0,home[0]); + menuItems = part?.length > 0 ? menuItems.concat(part) : menuItems; + } + + /* URL with openlink wont have sidebar and actions */ + if (history.location.pathname.includes("/openlink")) { + profileItem = ; + menuItems = menuItems.filter((ele) => ele.element === "LANGUAGE"); + } + return isMobile ? ( + } + isEmployee={isEmployee} + search={search} + setSearch={setSearch} + /> + ) : ( + + ); +}; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/EmployeeSideBar.js b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/EmployeeSideBar.js new file mode 100644 index 00000000000..55209f69924 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/EmployeeSideBar.js @@ -0,0 +1,144 @@ +import React, { useRef, useEffect, useState } from "react"; +import SubMenu from "./SubMenu"; +import { Loader, SearchIcon } from "@digit-ui/digit-ui-react-components"; +import { useTranslation } from "react-i18next"; + +const checkMatch=(path="",searchCriteria="")=>(path.toLowerCase().includes(searchCriteria.toLowerCase())) + + + +const EmployeeSideBar = () => { + const sidebarRef = useRef(null); + const { isLoading, data } = Digit.Hooks.useAccessControl(); + const [search, setSearch] = useState(""); + const { t } = useTranslation(); + useEffect(() => { + if (isLoading) { + return ; + } + sidebarRef.current.style.cursor = "pointer"; + collapseNav(); + }, [isLoading]); + + const expandNav = () => { + sidebarRef.current.style.width = "260px"; + sidebarRef.current.style.overflow = "auto"; + + sidebarRef.current.querySelectorAll(".dropdown-link").forEach((element) => { + element.style.display = "flex"; + }); + }; + const collapseNav = () => { + sidebarRef.current.style.width = "55px"; + sidebarRef.current.style.overflow = "hidden"; + + sidebarRef.current.querySelectorAll(".dropdown-link").forEach((element) => { + element.style.display = "none"; + }); + sidebarRef.current.querySelectorAll(".actions").forEach((element) => { + element.style.padding = "0"; + }); + }; + const configEmployeeSideBar = {}; + data?.actions + .filter((e) => e.url === "url") + .forEach((item) => { + let index = item?.path?.split(".")?.[0] || ""; + if (search == "" && item.path !== "") { + index = item.path.split(".")[0]; + if (!configEmployeeSideBar[index]) { + configEmployeeSideBar[index] = [item]; + } else { + configEmployeeSideBar[index].push(item); + } + } else if ( + checkMatch(t(`ACTION_TEST_${index?.toUpperCase()?.replace(/[ -]/g, "_")}`),search) || checkMatch(t(Digit.Utils.locale.getTransformedLocale(`ACTION_TEST_${item?.displayName}`)),search) + ) { + index = item.path.split(".")[0]; + if (!configEmployeeSideBar[index]) { + configEmployeeSideBar[index] = [item]; + } else { + configEmployeeSideBar[index].push(item); + } + } + }); + let res = []; + const splitKeyValue = () => { + const keys = Object.keys(configEmployeeSideBar); + keys?.sort((a, b) => a.orderNumber - b.orderNumber); + for (let i = 0; i < keys.length; i++) { + if (configEmployeeSideBar[keys[i]][0].path.indexOf(".") === -1) { + if (configEmployeeSideBar[keys[i]][0].displayName === "Home") { + const homeURL = `/${window?.contextPath}/employee`; + res.unshift({ + moduleName: keys[i].toUpperCase(), + icon: configEmployeeSideBar[keys[i]][0], + navigationURL: homeURL, + type: "single", + }); + } else { + res.push({ + moduleName: configEmployeeSideBar[keys[i]][0]?.displayName.toUpperCase(), + type: "single", + icon: configEmployeeSideBar[keys[i]][0], + navigationURL: configEmployeeSideBar[keys[i]][0].navigationURL, + }); + } + } else { + res.push({ + moduleName: keys[i].toUpperCase(), + links: configEmployeeSideBar[keys[i]], + icon: configEmployeeSideBar[keys[i]][0], + orderNumber: configEmployeeSideBar[keys[i]][0].orderNumber, + }); + } + } + if (res.find((a) => a.moduleName === "HOME")) { + const home = res?.filter((ob) => ob?.moduleName === "HOME"); + let res1 = res?.filter((ob) => ob?.moduleName !== "HOME"); + res = res1?.sort((a, b) => a.moduleName.localeCompare(b.moduleName)); + home?.[0] && res.unshift(home[0]); + } else { + res?.sort((a, b) => a.moduleName.localeCompare(b.moduleName)); + } + return res?.map((item, index) => { + // return ; + }); + }; + + if (isLoading) { + return ; + } + if (!res) { + return ""; + } + + const renderSearch = () => { + return ( +
+
+
+ + setSearch(e.target.value)} + /> +
+
+
+ ); + }; + + return ( +
+ {renderSearch()} + {splitKeyValue()} +
+ ); +}; + +export default EmployeeSideBar; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/StaticCitizenSideBar.js b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/StaticCitizenSideBar.js new file mode 100644 index 00000000000..88098a37b9c --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/StaticCitizenSideBar.js @@ -0,0 +1,247 @@ +import React, { useEffect, useState } from "react"; +import { + HomeIcon, + EditPencilIcon, + LogoutIcon, + Loader, + AddressBookIcon, + PropertyHouse, + CaseIcon, + CollectionIcon, + PTIcon, + OBPSIcon, + PGRIcon, + FSMIcon, + WSICon, + MCollectIcon, + Phone, + BirthIcon, + DeathIcon, + FirenocIcon, +} from "@digit-ui/digit-ui-react-components"; +import { Link, useLocation } from "react-router-dom"; +import SideBarMenu from "../../../config/sidebar-menu"; +import { useTranslation } from "react-i18next"; +import { useHistory } from "react-router-dom"; +import LogoutDialog from "../../Dialog/LogoutDialog"; +import ChangeCity from "../../ChangeCity"; +import { defaultImage } from "../../utils"; + + +/* +Feature :: Citizen Webview sidebar +*/ +const Profile = ({ info, stateName, t }) => ( +
+
+ +
+
+
{info?.name}
+
+
+
{info?.mobileNumber}
+
+ {info?.emailId && ( +
+
{info.emailId}
+
+ )} +
+ {window.location.href.includes("/employee") && + !window.location.href.includes("/employee/user/login") && + !window.location.href.includes("employee/user/language-selection") && } +
+); +const IconsObject = { + CommonPTIcon: , + OBPSIcon: , + propertyIcon: , + TLIcon: , + PGRIcon: , + FSMIcon: , + WSIcon: , + MCollectIcon: , + BillsIcon: , + BirthIcon: , + DeathIcon: , + FirenocIcon: , + HomeIcon: , + EditPencilIcon: , + LogoutIcon: , + Phone: , +}; +const StaticCitizenSideBar = ({ linkData, islinkDataLoading }) => { + const { t } = useTranslation(); + const history = useHistory(); + const location = useLocation(); + const { pathname } = location; + const { data: storeData, isFetched } = Digit.Hooks.useStore.getInitData(); + const { stateInfo } = storeData || {}; + const user = Digit.UserService.getUser(); + let isMobile = window.Digit.Utils.browser.isMobile(); + + const [isEmployee, setisEmployee] = useState(false); + const [isSidebarOpen, toggleSidebar] = useState(false); + const [showDialog, setShowDialog] = useState(false); + + const handleLogout = () => { + toggleSidebar(false); + setShowDialog(true); + }; + const handleOnSubmit = () => { + Digit.UserService.logout(); + setShowDialog(false); + }; + const handleOnCancel = () => { + setShowDialog(false); + }; + + if (islinkDataLoading) { + return ; + } + + const redirectToLoginPage = () => { + // localStorage.clear(); + // sessionStorage.clear(); + history.push(`/${window?.contextPath}/citizen/login`); + }; + const showProfilePage = () => { + history.push(`/${window?.contextPath}/citizen/user/profile`); + }; + + let menuItems = [...SideBarMenu(t, showProfilePage, redirectToLoginPage, isEmployee)]; + + menuItems = menuItems.filter((item) => item.element !== "LANGUAGE"); + + const tenantId = Digit.ULBService.getCurrentTenantId(); + const MenuItem = ({ item }) => { + const leftIconArray = item?.icon || item.icon?.type?.name; + const leftIcon = leftIconArray ? IconsObject[leftIconArray] : IconsObject.BillsIcon; + let itemComponent; + if (item.type === "component") { + itemComponent = item.action; + } else { + itemComponent = item.text; + } + const Item = () => ( + + {leftIcon} +
{itemComponent}
+
+ ); + if (item.type === "external-link") { + return ( + + + + ); + } + if (item.type === "link") { + return ( + + + + ); + } + + return ; + }; + let profileItem; + + if (isFetched && user && user.access_token) { + profileItem = ; + menuItems = menuItems.filter((item) => item?.id !== "login-btn"); + menuItems = [ + ...menuItems, + { + text: t("EDIT_PROFILE"), + element: "PROFILE", + icon: "EditPencilIcon", + populators: { + onClick: showProfilePage, + }, + }, + { + text: t("CORE_COMMON_LOGOUT"), + element: "LOGOUT", + icon: "LogoutIcon", + populators: { onClick: handleLogout }, + }, + { + text: ( + + {t("CS_COMMON_HELPLINE")} +
+ {storeData?.tenants.map((i) => { + i.code === tenantId ? ( + + ) : ( + + ); + })} + +
+
+ ), + element: "Helpline", + icon: "Phone", + }, + ]; + } + Object.keys(linkData) + ?.sort((x, y) => y.localeCompare(x)) + ?.map((key) => { + if (linkData[key][0]?.sidebar === `${window.contextPath}-links`) { + menuItems.splice(1, 0, { + type: linkData[key][0]?.sidebarURL?.includes(window?.contextPath) ? "link" : "external-link", + text: t(`ACTION_TEST_${Digit.Utils.locale.getTransformedLocale(key)}`), + links: linkData[key], + icon: linkData[key][0]?.leftIcon, + link: linkData[key][0]?.sidebarURL, + }); + } + }); + + return ( + +
+
+
+ {profileItem} +
+ {menuItems?.map((item, index) => ( +
+ +
+ ))} +
+
+
{showDialog && }
+
+
+ ); +}; + +export default StaticCitizenSideBar; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/SubMenu.js b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/SubMenu.js new file mode 100644 index 00000000000..048ea777a9f --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/SubMenu.js @@ -0,0 +1,193 @@ +import React, { useState, useContext } from "react"; +import { useHistory, useLocation, Link } from "react-router-dom"; +import { + ArrowForward, + ArrowVectorDown, + ArrowDirection, + HomeIcon, + ComplaintIcon, + BPAHomeIcon, + PropertyHouse, + CaseIcon, + ReceiptIcon, + PersonIcon, + DocumentIconSolid, + DropIcon, + CollectionsBookmarIcons, + FinanceChartIcon, + CollectionIcon, +} from "@digit-ui/digit-ui-react-components"; +import { useTranslation } from "react-i18next"; +import ReactTooltip from "react-tooltip"; + +const SubMenu = ({ item }) => { + const [subnav, setSubnav] = useState(false); + const location = useLocation(); + const { pathname } = location; + const { t } = useTranslation(); + const history = useHistory(); + + const showSubnav = () => setSubnav(!subnav); + const IconsObject = { + home: , + announcement: , + business: , + store: , + assignment: , + receipt: , + "business-center": , + description: , + "water-tap": , + "collections-bookmark": , + "insert-chart": , + edcr: , + collections: , + }; + const leftIconArray = item?.icon?.leftIcon?.split?.(":")?.[1] || item?.leftIcon?.split?.(":")[1]; + let leftIcon = IconsObject[leftIconArray] || IconsObject.collections; + const iconArr = item?.icon?.leftIcon?.split?.(":") || item?.leftIcon?.split?.(":"); + if (iconArr?.[0] == "dynamic") { + var IconComp = require("@digit-ui/digit-ui-react-components")?.[iconArr?.[1]]; + leftIcon = IconComp ? : leftIcon; + } + const getModuleName = item?.moduleName?.replace(/[ -]/g, "_"); + const appendTranslate = t(`ACTION_TEST_${getModuleName}`); + const trimModuleName = t(appendTranslate?.length > 20 ? appendTranslate.substring(0, 20) + "..." : appendTranslate); + + if (item.type === "single") { + const getOrigin = window.location.origin; + return ( +
+
+
+ history.push(`${ item.navigationURL}`)}>{leftIcon} + {item.navigationURL?.indexOf(`/${window?.contextPath}`) === -1 ? ( + + {trimModuleName} + + {trimModuleName?.includes("...") && ( + + {t(`ACTION_TEST_${getModuleName}`)} + + )} + + ) : ( + // + //
+ //

{trimModuleName}

+ // {t(`ACTION_TEST_${getModuleName}`)} + //
+ //
+ +
+ {trimModuleName} + + {trimModuleName?.includes("...") && ( + + {t(`ACTION_TEST_${getModuleName}`)} + + )} +
+ {/*
+

{trimModuleName}

+ {t(`ACTION_TEST_${getModuleName}`)} +
{" "} */} + + )} +
+
+
+ ); + } else { + return ( + +
+
ele?.url === "url" && pathname?.includes(ele?.navigationURL)) ? "active" : ""}`} + > +
+ {leftIcon} +
+ {trimModuleName} + {trimModuleName?.includes("...") && ( + + {t(`ACTION_TEST_${getModuleName}`)} + + )} +
+ {/*
+

{trimModuleName}

+ {t(`ACTION_TEST_${getModuleName}`)} +
{" "} */} +
+
{item.links && subnav ? : item.links ? : null}
+
+
+ + {subnav && + item.links + ?.sort((a, b) => a.orderNumber - b.orderNumber) + .filter((item) => item.url === "url" || item.url !== "") + .map((item, index) => { + const getChildName = item?.displayName?.toUpperCase()?.replace(/[ -]/g, "_"); + const appendTranslate = t(`ACTION_TEST_${getChildName}`); + const trimModuleName = t(appendTranslate?.length > 20 ? appendTranslate.substring(0, 20) + "..." : appendTranslate); + + if (item.navigationURL.indexOf(`/${window?.contextPath}`) === -1) { + const getOrigin = window.location.origin; + return ( + +
+ {trimModuleName} + {trimModuleName?.includes("...") && ( + + {t(`ACTION_TEST_${getChildName}`)} + + )} +
+ {/*
+
+

{trimModuleName}

+ {t(`ACTION_TEST_${getChildName}`)} +
{" "} +
*/} +
+ ); + } + return ( + +
+ {trimModuleName} + {trimModuleName?.includes("...") && ( + + {t(`ACTION_TEST_${getChildName}`)} + + )} + {/*
+

{trimModuleName}

+ {t(`ACTION_TEST_${getChildName}`)} +
{" "} */} +
+ + ); + })} +
+ ); + } +}; + +export default SubMenu; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/index.js b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/index.js new file mode 100644 index 00000000000..b59247623f9 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/TopBarSideBar/SideBar/index.js @@ -0,0 +1,23 @@ +import React from "react"; +import { CitizenSideBar } from "./CitizenSideBar"; +import EmployeeSideBar from "./EmployeeSideBar"; + +const SideBar = ({ t, CITIZEN, isSidebarOpen, toggleSidebar, handleLogout, mobileView, userDetails, modules, linkData, islinkDataLoading }) => { + if (CITIZEN) + return ( + + ); + else { + if (!mobileView && userDetails?.access_token) return ; + else return ; + } +}; + +export default SideBar; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/TopBarSideBar/TopBar.js b/micro-ui/web/packages/ui/src/components/TopBarSideBar/TopBar.js new file mode 100644 index 00000000000..19bfcefa4bc --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/TopBarSideBar/TopBar.js @@ -0,0 +1,156 @@ +import { Dropdown, Hamburger } from "@digit-ui/digit-ui-react-components"; +import React from "react"; +import { useHistory, useLocation } from "react-router-dom"; +import ChangeCity from "../ChangeCity"; +import ChangeLanguage from "../ChangeLanguage"; + +const TextToImg = (props) => ( + + {props?.name?.[0]?.toUpperCase()} + +); +const TopBar = ({ + t, + stateInfo, + toggleSidebar, + isSidebarOpen, + handleLogout, + userDetails, + CITIZEN, + cityDetails, + mobileView, + userOptions, + handleUserDropdownSelection, + logoUrl, + showLanguageChange = true, +}) => { + const [profilePic, setProfilePic] = React.useState(null); + + React.useEffect(async () => { + const tenant = Digit.ULBService.getCurrentTenantId(); + const uuid = userDetails?.info?.uuid; + if (uuid) { + const usersResponse = await Digit.UserService.userSearch(tenant, { uuid: [uuid] }, {}); + if (usersResponse && usersResponse.user && usersResponse.user.length) { + const userDetails = usersResponse.user[0]; + const thumbs = userDetails?.photo?.split(","); + setProfilePic(thumbs?.at(0)); + } + } + }, [profilePic !== null, userDetails?.info?.uuid]); + + const CitizenHomePageTenantId = Digit.ULBService.getCitizenCurrentTenant(true); + + let history = useHistory(); + const { pathname } = useLocation(); + + const conditionsToDisableNotificationCountTrigger = () => { + if (Digit.UserService?.getUser()?.info?.type === "EMPLOYEE") return false; + if (Digit.UserService?.getUser()?.info?.type === "CITIZEN") { + if (!CitizenHomePageTenantId) return false; + else return true; + } + return false; + }; + + const { data: { unreadCount: unreadNotificationCount } = {}, isSuccess: notificationCountLoaded } = Digit.Hooks.useNotificationCount({ + tenantId: CitizenHomePageTenantId, + config: { + enabled: conditionsToDisableNotificationCountTrigger(), + }, + }); + + const updateSidebar = () => { + if (!Digit.clikOusideFired) { + toggleSidebar(true); + } else { + Digit.clikOusideFired = false; + } + }; + + function onNotificationIconClick() { + history.push(`/${window?.contextPath}/citizen/engagement/notifications`); + } + + const urlsToDisableNotificationIcon = (pathname) => + !!Digit.UserService?.getUser()?.access_token + ? false + : [`/${window?.contextPath}/citizen/select-language`, `/${window?.contextPath}/citizen/select-location`].includes(pathname); + + if (CITIZEN) { + return ( +
+ : null} + /> +
+ ); + } + const loggedin = userDetails?.access_token ? true : false; + return ( +
+ {mobileView ? : null} + + + {loggedin && + (cityDetails?.city?.ulbGrade ? ( +

+ {t(cityDetails?.i18nKey).toUpperCase()}{" "} + {t(`ULBGRADE_${cityDetails?.city?.ulbGrade.toUpperCase().replace(" ", "_").replace(".", "_")}`).toUpperCase()} +

+ ) : ( + + ))} + {!loggedin && ( +

+ {t(`MYCITY_${stateInfo?.code?.toUpperCase()}_LABEL`)} {t(`MYCITY_STATECODE_LABEL`)} +

+ )} + {!mobileView && ( +
+
+ {!window.location.href.includes("employee/user/login") && !window.location.href.includes("employee/user/language-selection") && ( + + )} +
+
{showLanguageChange && }
+ {userDetails?.access_token && ( +
+ + ) : ( + + ) + } + /> +
+ )} + +
+ )} +
+
+ ); +}; + +export default TopBar; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/TopBarSideBar/index.js b/micro-ui/web/packages/ui/src/components/TopBarSideBar/index.js new file mode 100644 index 00000000000..d18b9139b97 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/TopBarSideBar/index.js @@ -0,0 +1,79 @@ +import React, { useState } from "react"; +import { EditPencilIcon, LogoutIcon } from "@digit-ui/digit-ui-react-components"; +import TopBar from "./TopBar"; +import { useHistory } from "react-router-dom"; +import SideBar from "./SideBar"; +import LogoutDialog from "../Dialog/LogoutDialog"; + +const TopBarSideBar = ({ + t, + stateInfo, + userDetails, + CITIZEN, + cityDetails, + mobileView, + handleUserDropdownSelection, + logoUrl, + showSidebar = true, + showLanguageChange, + linkData, + islinkDataLoading, +}) => { + const [isSidebarOpen, toggleSidebar] = useState(false); + const history = useHistory(); + const [showDialog, setShowDialog] = useState(false); + const handleLogout = () => { + toggleSidebar(false); + setShowDialog(true); + }; + const handleOnSubmit = () => { + Digit.UserService.logout(); + setShowDialog(false); + } + const handleOnCancel = () => { + setShowDialog(false); + } + const userProfile = () => { + history.push(`/${window?.contextPath}/employee/user/profile`); + }; + const userOptions = [ + { name: t("EDIT_PROFILE"), icon: , func: userProfile }, + { name: t("CORE_COMMON_LOGOUT"), icon: , func: handleLogout }, + ]; + return ( + + {/* */} + {showDialog && ( + + )} + {/* {showSidebar && ( + + )} */} + + ); +}; +export default TopBarSideBar; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/index.js b/micro-ui/web/packages/ui/src/components/index.js new file mode 100644 index 00000000000..09c8de3c8f3 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/index.js @@ -0,0 +1,7 @@ +import Loader from "./Loader"; + + +export{ + Loader + +} \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/components/utils.js b/micro-ui/web/packages/ui/src/components/utils.js new file mode 100644 index 00000000000..44dfca5e7b2 --- /dev/null +++ b/micro-ui/web/packages/ui/src/components/utils.js @@ -0,0 +1,22 @@ +export const defaultImage = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAO4AAADUCAMAAACs0e/bAAAAM1BMVEXK0eL" + + "/" + + "/" + + "/" + + "/Dy97GzuD4+fvL0uPg5O7T2efb4OvR1+Xr7vTk5/Df4+37/P3v8fbO1eTt8PUsnq5FAAAGqElEQVR4nO2d25ajIBBFCajgvf/" + + "/a0eMyZgEjcI5xgt7Hmatme507UaxuJXidiDqjmSgeVIMlB1ZR1WZAf2gbdu0QwixSYzjOJPmHurfEGEfY9XzjNGG9whQCeVAuv5xQEySLtR9hPuIcwj0EeroN5m3D1IbsbgHK0esiQ9MKs" + + "qXVr8Hm/a/Pulk6wihpCIXBw3dh7bTvRBt9+dC5NfS1VH3xETdM3MxXRN1T0zUPTNR98xcS1dlV9NNfx3DhkTdM6PKqHteVBF1z0vU5f0sKdpc2zWLKutXrjJjdLvpesRmukqYonauPhXpds" + + "Lb6CppmpnltsYIuY2yavi6Mi2/rzAWm1zUfF0limVLqkZyA+mDYevKBS37aGC+L1lX5e7uyU1Cv565uiua9k5LFqbqqrnu2I3m+jJ11ZoLeRtfmdB0Uw/ZDsP0VTxdn7a1VERfmq7Xl" + + "Xyn5D2QWLoq8bZlPoBJumphJjVBw/Ll6CoTZGsTDs4NrGqKbqBth8ZHJUi6cn168QmleSm6GmB7Kxm+6obXlf7PoDHosCwM3QpiS2legi6ocSl3L0G3BdneDDgwQdENfeY+SfDJBkF37Z" + + "B+GvwzA6/rMaafAn8143VhPZWdjMWG1oHXhdnemgPoAvLlB/iZyRTfVeF06wPoQhJmlm4bdcOAZRlRN5gcPc5SoPEQR1fDdbOo6wn+uYvXxY0QCLom6gYROKH+Aj5nvphuFXWDiLpRdxl" + + "/19LFT95k6CHCrnW7pCDqBn1i1PUFvii2c11oZOJ6usWeH0RRNzC4Zs+6FTi2nevCVwCjbugnXklX5fkfTldL8PEilUB1kfNyN1u9MME2sATr4lbuB7AjfLAuvsRm1A0g6gYRdcPAjvBlje" + + "2Z8brI8OC68AcRdlCkwLohx2mcZMjw9q+LzarQurjtnwPYAydX08WecECO/u6Ad0GBdYG7jO5gB4Ap+PwKcA9ZT43dn4/W9TyiPAn4OAJaF7h3uwe8StSCddFdM3jqFa2LvnnB5zzhuuBBAj" + + "Y4gi50cg694gnXhTYvfMdrjtcFZhrwE9r41gUem8IXWMC3LrBzxh+a0gRd1N1LOK7M0IUUGuggvEmHoStA2/MJh7MpupiDU4TzjhxdzLAoO4ouZvqVURbFMHQlZD6SUeWHoguZsSLUGegreh" + + "A+FZFowPdUWTi6iMoZlIpGGUUXkDbjj/9ZOLqAQS/+GIKl5BQOCn/ycqpzkXSDm5dU7ZWkG7wUyGlcmm7g5Ux56AqirgoaJ7BeokPTDbp9CbVunjFxPrl7+HqnkrSq1Da7JX20f3dV8yJi6v" + + "oO81mX8vV0mx3qUsZCPRfTlVRdz2EvdufYGDvNQvvwqHtmXd+a1ITinwNcXc+lT6JuzdT1XDyBn/x7wtX1HCQQdW9MXc8xArGrirowfLeUEbMqqq6f7TF1lfRdOuGNiGi6SpT+WxY06xUfNN" + + "2wBfyE9I4tlm7w5hvOPDNJN3yNiLMipji6gE3chKhouoCtN5x3QlF0EZt8OW/8ougitqJQlk1aii7iFC9l0MvRReyao7xNjKML2Z/PuHlzhi5mFxljiZeiC9rPTEisNEMX9KYAwo5Xhi7qaA" + + "3hamboYm7dG+NVrXhdaYDv5zFaQZsYrCtbbAGnjkQDX2+J1FXCwOsqWOpKoIQNTFdqYBWydxqNqUoG0pVpCS+H8kaJaGKErlIaXj7CRRE+gRWuKwW9YZ80oVOUgbpdT0zpnSZJTIiwCtJVelv" + + "Xntr4P5j6BWfPb5Wcx84C4cq3hb11lco2u2Mdwp6XdJ/Ne3wb8DWdfiRenZaXrhLwOj4e+GQeHroy3YOspS7TlU28Wle2m2QUS0mqdcbrdNW+ZHsSsyK7tBfm0q/dWcv+Z3mytVx3t7KWulq" + + "Ue6ilunu8jF8pFwgv1FXp3mUt35OtRbr7eM4u4Gs6vUBXgeuHc5kfE/cbvWZtkROLm1DMtLCy80tzsu2PRj0hTI8fvrQuvsjlJkyutszq+m423wHaLTyniy/XuiGZ84LuT+m5ZfNfRxyGs7L" + + "XZOvia7VujatUwVTrIt+Q/Csc7Tuhe+BOakT10b4TuoiiJjvgU9emTO42PwEfBa+cuodKkuf42DXr1D3JpXz73Hnn0j10evHKe+nufgfUm+7B84sX9FfdEzXux2DBpWuKokkCqN/5pa/8pmvn" + + "L+RGKCddCGmatiPyPB/+ekO/M/q/7uvbt22kTt3zEnXPzCV13T3Gel4/6NduDu66xRvlPNkM1RjjxUdv+4WhGx6TftD19Q/dfzpwcHO+rE3fAAAAAElFTkSuQmCC"; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/config/sidebar-menu.js b/micro-ui/web/packages/ui/src/config/sidebar-menu.js new file mode 100644 index 00000000000..0a81de44510 --- /dev/null +++ b/micro-ui/web/packages/ui/src/config/sidebar-menu.js @@ -0,0 +1,33 @@ +import React from "react"; +import { HomeIcon, LanguageIcon, LogoutIcon, AddressBookIcon, LocationIcon } from "@digit-ui/digit-ui-react-components"; +import ChangeLanguage from "../components/ChangeLanguage"; + +const SideBarMenu = (t, closeSidebar, redirectToLoginPage, isEmployee) => [ + { + type: "link", + element: "HOME", + text: t("COMMON_BOTTOM_NAVIGATION_HOME"), + link: isEmployee ? `/${window?.contextPath}/employee` : `/${window?.contextPath}/citizen`, + icon: "HomeIcon", + populators: { + onClick: closeSidebar, + }, + }, + { + type: "component", + element: "LANGUAGE", + action: , + icon: "LanguageIcon", + }, + { + id: "login-btn", + element: "LOGIN", + text: t("CORE_COMMON_LOGIN"), + icon: , + populators: { + onClick: redirectToLoginPage, + }, + }, +]; + +export default SideBarMenu; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/context/index.js b/micro-ui/web/packages/ui/src/context/index.js new file mode 100644 index 00000000000..797dc7e7479 --- /dev/null +++ b/micro-ui/web/packages/ui/src/context/index.js @@ -0,0 +1,3 @@ +import React from "react"; + +export const ComponentProvider = React.createContext(); \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/hooks/index.js b/micro-ui/web/packages/ui/src/hooks/index.js new file mode 100644 index 00000000000..b6ec5abf0df --- /dev/null +++ b/micro-ui/web/packages/ui/src/hooks/index.js @@ -0,0 +1,28 @@ +import utils from "../utils"; +import useLocalization from "./useLocalization"; + +const core = { + sampleCoreHook: () => {}, + useLocalization +}; + +const Hooks = { + core, +}; + +const Utils = { + browser: { + sample: () => {}, + }, + core: { + ...utils, + sampleUtil: () => {}, + initCore: () => { + }, + }, +}; + +export const CustomisedHooks = { + Hooks, + Utils, +}; diff --git a/micro-ui/web/packages/ui/src/hooks/trans.js b/micro-ui/web/packages/ui/src/hooks/trans.js new file mode 100644 index 00000000000..ff74ef2f478 --- /dev/null +++ b/micro-ui/web/packages/ui/src/hooks/trans.js @@ -0,0 +1,63 @@ +import i18next from "i18next"; +import { initReactI18next } from "react-i18next"; +import ReactPostprocessor from "i18next-react-postprocessor"; + +const i18nextConfig = () => ({ + lng: Digit.StoreData.getCurrentLanguage(), + fallbackLng: "en_IN", + debug: true, + ns: ["translations"], + defaultNS: "translations", + keySeparator: false, + saveMissing: false, + saveMissingTo: "current", + interpolation: { + escapeValue: false, + formatSeparator: ",", + }, + postProcess: [`reactPostprocessor`, "templatePostprocessor"], + react: { + useSuspense: true, + bindI18n: "loaded", + bindI18nStore: "added", + }, + resources: { + en_IN: { + translations: { + welcome: "Welcome", + }, + }, + }, +}); + +function replaceLiterals(text = "", dynamicValues = {}) { + let returnText = text; + const regex = /[^\{\{][\{]\w+/; + if (regex.exec(text) !== null) { + Object.keys(dynamicValues).forEach((key) => { + returnText = returnText.replace(`{${key.toUpperCase()}}`, dynamicValues[key]); + }); + } + + return returnText; +} + +const templatePostprocessor = { + type: "postProcessor", + name: "templatePostprocessor", + process: function (value, key, options, translator) { + return replaceLiterals(value, options); + }, +}; + +export const initI18n = (callback) => { + return i18next + .use(new ReactPostprocessor()) + .use(templatePostprocessor) + .use(initReactI18next) + .init(i18nextConfig(), (err,t) => { + if (err) return console.log('something went wrong loading', err); + window.i18next = i18next; + callback(); + }); +}; diff --git a/micro-ui/web/packages/ui/src/hooks/useAuth.js b/micro-ui/web/packages/ui/src/hooks/useAuth.js new file mode 100644 index 00000000000..3fdc635ec41 --- /dev/null +++ b/micro-ui/web/packages/ui/src/hooks/useAuth.js @@ -0,0 +1,31 @@ +import { useCallback, useEffect } from "react"; +import { useHistory } from "react-router-dom"; +import { History } from "history"; +import { BehaviorSubject, Observable } from "rxjs"; + +const isSignedIn$ = new BehaviorSubject(false); + +const useAuth = () => { + const history = useHistory(); + + useEffect(() => { + const subscription = isSignedIn$.subscribe((val) => { + if (val) { + history.push("/dashboard"); + } else if (history.location.pathname === "/dashboard") { + history.push("/"); + } + }); + + return () => { + subscription.unsubscribe(); + }; + }, []); + + const login = useCallback(() => isSignedIn$.next(true), []); + const logout = useCallback(() => isSignedIn$.next(false), []); + + return { login, logout, history, isSignedIn$: isSignedIn$.asObservable() }; +}; + +export default useAuth; diff --git a/micro-ui/web/packages/ui/src/hooks/useInterval.js b/micro-ui/web/packages/ui/src/hooks/useInterval.js new file mode 100644 index 00000000000..c914d1ce1fb --- /dev/null +++ b/micro-ui/web/packages/ui/src/hooks/useInterval.js @@ -0,0 +1,20 @@ +import React, { useEffect, useRef } from "react"; + +function useInterval(callback, delay) { + const savedCallback = useRef(); + useEffect(() => { + savedCallback.current = callback; + }, [callback]); + + useEffect(() => { + function tick() { + savedCallback.current(); + } + if (delay !== null) { + const timer = setInterval(tick, delay); + return () => clearInterval(timer); + } + }, [delay]); +} + +export default useInterval; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/hooks/useLocalization.js b/micro-ui/web/packages/ui/src/hooks/useLocalization.js new file mode 100644 index 00000000000..c9eb41462ed --- /dev/null +++ b/micro-ui/web/packages/ui/src/hooks/useLocalization.js @@ -0,0 +1,113 @@ +import { useQuery, useQueryClient } from "react-query"; + +const TransformArrayToObj = (traslationList) => { + if(!traslationList){ + return {} + } + return traslationList.reduce( + // eslint-disable-next-line + (obj, item) => ((obj[item.code] = item.message), obj), + {} + ); + // return trasformedTraslation; +}; + +const consolidateObjectsByKey = (arr, key="module") => { + const result = {}; + + arr.forEach(obj => { + const keyValue = obj[key]; + if (!result[keyValue]) { + result[keyValue] = []; + } + result[keyValue].push(obj); + }); + + return result; +} + +const LocalisationSearchUtil = async ({ url, params, body, plainAccessRequest,state } ) => { + const data = await Digit.CustomService.getResponse({ url, params, body, plainAccessRequest }) + return data; +} + +const LocalisationSearch = { + fetchResults : async ({ url, params, body, plainAccessRequest,state }) => { + try { + //here check if the data is in session storage or not + //Digit.module.locale.tenant + //here if moduleName has commas in it that means multiple modules are requested + //separate out the modules and set separately + + //basically here what we are doing is caching the already fetched modules in localStorage so that we don't have to make api calls to localization service if same modules are requested more than once + const {locale,module:moduleName,tenantId} = params || {} + const modulesRequested = moduleName.split(",") + const modulesToRequest = [] + let consolidatedResponse = {} + + const modulesAlreadyPresent = modulesRequested.filter(mod => { + if(localStorage.getItem(`Digit.${tenantId}.${mod}.${locale}`) && Object.keys(localStorage.getItem(`Digit.${tenantId}.${mod}.${locale}`)).length > 0){ + consolidatedResponse = {...consolidatedResponse,...JSON.parse(localStorage.getItem(`Digit.${tenantId}.${mod}.${locale}`))} + return true + }else{ + modulesToRequest.push(mod) + return false + } + }) + + const modulesToRequestStr = modulesToRequest.join(',') + if(modulesToRequest.length===0) { + return consolidatedResponse + } + + params.module = modulesToRequestStr + + const response = await LocalisationSearchUtil({ url, params, body, plainAccessRequest,state }) + //before transforming we need to separate out the messages based on module key and then set them on storage before transforming them + const transformedResponseBasedOnModule = consolidateObjectsByKey(response.messages) + modulesToRequest.forEach(mod => { + + const transformedResponse = TransformArrayToObj(transformedResponseBasedOnModule?.[mod]) + consolidatedResponse = {...consolidatedResponse,...transformedResponse} + //setting messages corresponding to each module requested + localStorage.setItem(`Digit.${tenantId}.${mod}.${locale}`,JSON.stringify(transformedResponse)) + }) + return consolidatedResponse + } catch (error) { + throw new Error(error.message); + } + } +} + +const useLocalization = ({url="/localization/messages/v1/_search", params, body, config = {}, plainAccessRequest,changeQueryName="Random",state,i18n }) => { + const { isLoading, data, isFetching,refetch,error } = useQuery( + [url,params,i18n.language].filter((e) => e), + () => LocalisationSearch.fetchResults({ url, params, body, plainAccessRequest,state }), + { + cacheTime:Infinity, + staleTime:Infinity, + select:(data) => { + i18n.addResourceBundle(i18n.language, 'translations', data); + //example code to add hindi msg to debug change lang if not upserted + // i18n.addResourceBundle('hi_IN', 'translations', { + // CORE_COMMON_PROFILE_NAME:"here" + // }); + + return [] + }, + ...config, + } + ); + + return { + isLoading, + isFetching, + data, + refetch, + error + }; +}; + + + +export default useLocalization; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/hooks/useRouter.js b/micro-ui/web/packages/ui/src/hooks/useRouter.js new file mode 100644 index 00000000000..5dd6a9e19fe --- /dev/null +++ b/micro-ui/web/packages/ui/src/hooks/useRouter.js @@ -0,0 +1,17 @@ +import { useHistory } from "react-router-dom"; +import { useCallback } from "react"; + + +const useRouter = () => { + const history = useHistory(); + + const navigate = useCallback((route) => { + if (route !== history.location.pathname) { + history.push(route); + } + }, []); + + return { navigate }; +}; + +export default useRouter; diff --git a/micro-ui/web/packages/ui/src/index.js b/micro-ui/web/packages/ui/src/index.js new file mode 100644 index 00000000000..50599f7ad8e --- /dev/null +++ b/micro-ui/web/packages/ui/src/index.js @@ -0,0 +1 @@ +import("./bootstrap"); diff --git a/micro-ui/web/packages/ui/src/locale/i18n.js b/micro-ui/web/packages/ui/src/locale/i18n.js new file mode 100644 index 00000000000..a0fb7eeb173 --- /dev/null +++ b/micro-ui/web/packages/ui/src/locale/i18n.js @@ -0,0 +1,42 @@ +import i18n from 'i18next'; +import LanguageDetector from 'i18next-browser-languagedetector'; +import { initReactI18next } from 'react-i18next'; +import ReactPostprocessor from "i18next-react-postprocessor"; + +function replaceLiterals(text = "", dynamicValues = {}) { + let returnText = text; + const regex = /[^\{\{][\{]\w+/; + if (regex.exec(text) !== null) { + Object.keys(dynamicValues).forEach((key) => { + returnText = returnText.replace(`{${key.toUpperCase()}}`, dynamicValues[key]); + }); + } + + return returnText; +} + +const templatePostprocessor = { + type: "postProcessor", + name: "templatePostprocessor", + process: function (value, key, options, translator) { + return replaceLiterals(value, options); + }, +}; + + +i18n + .use(new ReactPostprocessor()) + .use(templatePostprocessor) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + ns: ["translations"], + defaultNS: "translations", + debug: false, + lng: localStorage.getItem("i18nextLng") || "en_IN", + fallbackLng:"en_IN", + returnObjects:true, + postProcess: [`reactPostprocessor`, "templatePostprocessor"], + resources: { + }, + }); \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/AllServices/index.js b/micro-ui/web/packages/ui/src/pages/citizen/AllServices/index.js new file mode 100644 index 00000000000..7ce0ae70016 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/AllServices/index.js @@ -0,0 +1,26 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { AppModules } from "../../../components/AppModules"; + +const CitizenApp = ({ + stateInfo, + userDetails, + CITIZEN, + cityDetails, + mobileView, + handleUserDropdownSelection, + logoUrl, + DSO, + stateCode="pg", + modules, + appTenants, + sourceUrl, + pathname, +}) => { + + const { t } = useTranslation(); + + return ; +}; + +export default CitizenApp; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/FAQs/FAQs.js b/micro-ui/web/packages/ui/src/pages/citizen/FAQs/FAQs.js new file mode 100644 index 00000000000..2e64aaffc6f --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/FAQs/FAQs.js @@ -0,0 +1,39 @@ +import { BackButton, Header, Loader, SearchIconSvg } from "@digit-ui/digit-ui-react-components"; +import React, { Fragment } from "react"; +import { useTranslation } from "react-i18next"; +import FaqComponent from "./FaqComponent"; + +const FAQsSection = ({ module }) => { + const user = Digit.UserService.getUser(); + const tenantId = user?.info?.tenantId || Digit.ULBService.getCurrentTenantId(); + const { t } = useTranslation(); + + const SearchImg = () => { + return ; + }; + + const { isLoading, data } = Digit.Hooks.useGetFAQsJSON(Digit.ULBService.getStateId()); + + const moduleFaqs = data?.MdmsRes["common-masters"]?.faqs[0]?.[`${module}`].faqs; + + if (isLoading) { + return ; + } + return ( + +
+ +
+
{t("FAQ_S")}
+
+
+ {moduleFaqs.map((faq, i) => ( + + ))} +
+
+
+ ); +}; + +export default FAQsSection; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/FAQs/FaqComponent.js b/micro-ui/web/packages/ui/src/pages/citizen/FAQs/FaqComponent.js new file mode 100644 index 00000000000..0737f1f1283 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/FAQs/FaqComponent.js @@ -0,0 +1,27 @@ +import { ArrowForward } from "@digit-ui/digit-ui-react-components"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; + +const FaqComponent = (props) => { + const { question, answer, lastIndex } = props; + const [isOpen, toggleOpen] = useState(false); + const { t } = useTranslation(); + + return ( +
toggleOpen(!isOpen)}> +
+ {t(question)} + + {isOpen ? : } + +
+ +
+ {t(answer)} +
+ {!lastIndex ?
: null} +
+ ); +}; + +export default FaqComponent; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Home/ImageUpload/UploadDrawer.js b/micro-ui/web/packages/ui/src/pages/citizen/Home/ImageUpload/UploadDrawer.js new file mode 100644 index 00000000000..425f636e50b --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Home/ImageUpload/UploadDrawer.js @@ -0,0 +1,96 @@ +import React, { useState, useEffect } from "react"; +import { GalleryIcon, RemoveIcon, UploadFile } from "@digit-ui/digit-ui-react-components"; +import { useTranslation } from "react-i18next"; + +function UploadDrawer({ setProfilePic, closeDrawer, userType, removeProfilePic ,showToast}) { + const [uploadedFile, setUploadedFile] = useState(null); + const [file, setFile] = useState(""); + const [error, setError] = useState(null); + const { t } = useTranslation(); + const selectfile = (e) => setFile(e.target.files[0]); + const removeimg = () => {removeProfilePic(); closeDrawer()}; + const onOverlayBodyClick = () => closeDrawer(); + + useEffect(() => { + (async () => { + setError(null); + if (file) { + if (file.size >= 1000000) { + showToast("error", t("CORE_COMMON_PROFILE_MAXIMUM_UPLOAD_SIZE_EXCEEDED")) + setError(t("CORE_COMMON_PROFILE_MAXIMUM_UPLOAD_SIZE_EXCEEDED")); + } else { + try { + const response = await Digit.UploadServices.Filestorage(`${userType}-profile`, file, Digit.ULBService.getStateId()); + if (response?.data?.files?.length > 0) { + const fileStoreId = response?.data?.files[0]?.fileStoreId; + setUploadedFile(fileStoreId); + setProfilePic(fileStoreId); + } else { + showToast("error", t("CORE_COMMON_PROFILE_FILE_UPLOAD_ERROR")) + setError(t("CORE_COMMON_PROFILE_FILE_UPLOAD_ERROR")); + } + } catch (err) { + showToast("error",t("CORE_COMMON_PROFILE_INVALID_FILE_INPUT")) + // setError(t("PT_FILE_UPLOAD_ERROR")); + } + } + } + })(); + }, [file]); + + return ( + +
+
+
+ + + +
+ +
+ + +
+
+
+ ); +} + +export default UploadDrawer; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Home/LanguageSelection.js b/micro-ui/web/packages/ui/src/pages/citizen/Home/LanguageSelection.js new file mode 100644 index 00000000000..02790e362d3 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Home/LanguageSelection.js @@ -0,0 +1,48 @@ +import React, { useMemo } from "react"; +import { PageBasedInput, Loader, RadioButtons, CardHeader } from "@digit-ui/digit-ui-react-components"; +import { useTranslation } from "react-i18next"; +import { useHistory } from "react-router-dom"; + +const LanguageSelection = () => { + const { t } = useTranslation(); + const history = useHistory(); + + const { data: { languages, stateInfo } = {}, isLoading } = Digit.Hooks.useStore.getInitData(); + const selectedLanguage = Digit.StoreData.getCurrentLanguage(); + + const texts = useMemo( + () => ({ + header: t("CS_COMMON_CHOOSE_LANGUAGE"), + submitBarLabel: t("CORE_COMMON_CONTINUE"), + }), + [t] + ); + + const RadioButtonProps = useMemo( + () => ({ + options: languages, + optionsKey: "label", + additionalWrapperClass: "reverse-radio-selection-wrapper", + onSelect: (language) => Digit.LocalizationService.changeLanguage(language.value, stateInfo.code), + selectedOption: languages?.filter((i) => i.value === selectedLanguage)[0], + }), + [selectedLanguage, languages] + ); + + function onSubmit() { + history.push(`/${window?.contextPath}/citizen/select-location`); + } + + return isLoading ? ( + + ) : ( +
+ + {t("CS_COMMON_CHOOSE_LANGUAGE")} + + +
+ ); +}; + +export default LanguageSelection; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Home/LocationSelection.js b/micro-ui/web/packages/ui/src/pages/citizen/Home/LocationSelection.js new file mode 100644 index 00000000000..b04fd64cb03 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Home/LocationSelection.js @@ -0,0 +1,64 @@ +import { BackButton, CardHeader, CardLabelError, PageBasedInput, SearchOnRadioButtons } from "@digit-ui/digit-ui-react-components"; +import React, { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useHistory, useLocation } from "react-router-dom"; + +const LocationSelection = () => { + const { t } = useTranslation(); + const history = useHistory(); + const location = useLocation(); + const { data: cities, isLoading } = Digit.Hooks.useTenants(); + + const [selectedCity, setSelectedCity] = useState(() => ({ code: Digit.ULBService.getCitizenCurrentTenant(true) })); + const [showError, setShowError] = useState(false); + + const texts = useMemo( + () => ({ + header: t("CS_COMMON_CHOOSE_LOCATION"), + submitBarLabel: t("CORE_COMMON_CONTINUE"), + }), + [t] + ); + + function selectCity(city) { + setSelectedCity(city); + setShowError(false); + } + + const RadioButtonProps = useMemo(() => { + return { + options: cities, + optionsKey: "i18nKey", + additionalWrapperClass: "reverse-radio-selection-wrapper", + onSelect: selectCity, + selectedOption: selectedCity, + }; + }, [cities, t, selectedCity]); + + function onSubmit() { + if (selectedCity) { + Digit.SessionStorage.set("CITIZEN.COMMON.HOME.CITY", selectedCity); + const redirectBackTo = location.state?.redirectBackTo; + if (redirectBackTo) { + history.replace(redirectBackTo); + } else history.push(`/${window?.contextPath}/citizen`); + } else { + setShowError(true); + } + } + + return isLoading ? ( + + ) : ( +
+ + + {t("CS_COMMON_CHOOSE_LOCATION")} + + {showError ? {t("CS_COMMON_LOCATION_SELECTION_ERROR")} : null} + +
+ ); +}; + +export default LocationSelection; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Home/UserProfile.js b/micro-ui/web/packages/ui/src/pages/citizen/Home/UserProfile.js new file mode 100644 index 00000000000..1aae580f342 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Home/UserProfile.js @@ -0,0 +1,704 @@ +import { + CameraIcon, + CardLabel, + Dropdown, + LabelFieldPair, + MobileNumber, + TextInput, + Toast, + CardLabelError, + BreadCrumb, + BackButton, + Loader, + SubmitBar + } from "@digit-ui/digit-ui-react-components"; + import React, { useEffect, useState } from "react"; + import { useTranslation } from "react-i18next"; + import { useHistory } from "react-router-dom"; + import UploadDrawer from "./ImageUpload/UploadDrawer"; + + const defaultImage = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAO4AAADUCAMAAACs0e/bAAAAM1BMVEXK0eL" + + "/" + + "/" + + "/" + + "/Dy97GzuD4+fvL0uPg5O7T2efb4OvR1+Xr7vTk5/Df4+37/P3v8fbO1eTt8PUsnq5FAAAGqElEQVR4nO2d25ajIBBFCajgvf/" + + "/a0eMyZgEjcI5xgt7Hmatme507UaxuJXidiDqjmSgeVIMlB1ZR1WZAf2gbdu0QwixSYzjOJPmHurfEGEfY9XzjNGG9whQCeVAuv5xQEySLtR9hPuIcwj0EeroN5m3D1IbsbgHK0esiQ9MKs" + + "qXVr8Hm/a/Pulk6wihpCIXBw3dh7bTvRBt9+dC5NfS1VH3xETdM3MxXRN1T0zUPTNR98xcS1dlV9NNfx3DhkTdM6PKqHteVBF1z0vU5f0sKdpc2zWLKutXrjJjdLvpesRmukqYonauPhXpds" + + "Lb6CppmpnltsYIuY2yavi6Mi2/rzAWm1zUfF0limVLqkZyA+mDYevKBS37aGC+L1lX5e7uyU1Cv565uiua9k5LFqbqqrnu2I3m+jJ11ZoLeRtfmdB0Uw/ZDsP0VTxdn7a1VERfmq7Xl" + + "Xyn5D2QWLoq8bZlPoBJumphJjVBw/Ll6CoTZGsTDs4NrGqKbqBth8ZHJUi6cn168QmleSm6GmB7Kxm+6obXlf7PoDHosCwM3QpiS2legi6ocSl3L0G3BdneDDgwQdENfeY+SfDJBkF37Z" + + "B+GvwzA6/rMaafAn8143VhPZWdjMWG1oHXhdnemgPoAvLlB/iZyRTfVeF06wPoQhJmlm4bdcOAZRlRN5gcPc5SoPEQR1fDdbOo6wn+uYvXxY0QCLom6gYROKH+Aj5nvphuFXWDiLpRdxl" + + "/19LFT95k6CHCrnW7pCDqBn1i1PUFvii2c11oZOJ6usWeH0RRNzC4Zs+6FTi2nevCVwCjbugnXklX5fkfTldL8PEilUB1kfNyN1u9MME2sATr4lbuB7AjfLAuvsRm1A0g6gYRdcPAjvBlje" + + "2Z8brI8OC68AcRdlCkwLohx2mcZMjw9q+LzarQurjtnwPYAydX08WecECO/u6Ad0GBdYG7jO5gB4Ap+PwKcA9ZT43dn4/W9TyiPAn4OAJaF7h3uwe8StSCddFdM3jqFa2LvnnB5zzhuuBBAj" + + "Y4gi50cg694gnXhTYvfMdrjtcFZhrwE9r41gUem8IXWMC3LrBzxh+a0gRd1N1LOK7M0IUUGuggvEmHoStA2/MJh7MpupiDU4TzjhxdzLAoO4ouZvqVURbFMHQlZD6SUeWHoguZsSLUGegreh" + + "A+FZFowPdUWTi6iMoZlIpGGUUXkDbjj/9ZOLqAQS/+GIKl5BQOCn/ycqpzkXSDm5dU7ZWkG7wUyGlcmm7g5Ux56AqirgoaJ7BeokPTDbp9CbVunjFxPrl7+HqnkrSq1Da7JX20f3dV8yJi6v" + + "oO81mX8vV0mx3qUsZCPRfTlVRdz2EvdufYGDvNQvvwqHtmXd+a1ITinwNcXc+lT6JuzdT1XDyBn/x7wtX1HCQQdW9MXc8xArGrirowfLeUEbMqqq6f7TF1lfRdOuGNiGi6SpT+WxY06xUfNN" + + "2wBfyE9I4tlm7w5hvOPDNJN3yNiLMipji6gE3chKhouoCtN5x3QlF0EZt8OW/8ougitqJQlk1aii7iFC9l0MvRReyao7xNjKML2Z/PuHlzhi5mFxljiZeiC9rPTEisNEMX9KYAwo5Xhi7qaA" + + "3hamboYm7dG+NVrXhdaYDv5zFaQZsYrCtbbAGnjkQDX2+J1FXCwOsqWOpKoIQNTFdqYBWydxqNqUoG0pVpCS+H8kaJaGKErlIaXj7CRRE+gRWuKwW9YZ80oVOUgbpdT0zpnSZJTIiwCtJVelv" + + "Xntr4P5j6BWfPb5Wcx84C4cq3hb11lco2u2Mdwp6XdJ/Ne3wb8DWdfiRenZaXrhLwOj4e+GQeHroy3YOspS7TlU28Wle2m2QUS0mqdcbrdNW+ZHsSsyK7tBfm0q/dWcv+Z3mytVx3t7KWulq" + + "Ue6ilunu8jF8pFwgv1FXp3mUt35OtRbr7eM4u4Gs6vUBXgeuHc5kfE/cbvWZtkROLm1DMtLCy80tzsu2PRj0hTI8fvrQuvsjlJkyutszq+m423wHaLTyniy/XuiGZ84LuT+m5ZfNfRxyGs7L" + + "XZOvia7VujatUwVTrIt+Q/Csc7Tuhe+BOakT10b4TuoiiJjvgU9emTO42PwEfBa+cuodKkuf42DXr1D3JpXz73Hnn0j10evHKe+nufgfUm+7B84sX9FfdEzXux2DBpWuKokkCqN/5pa/8pmvn" + + "L+RGKCddCGmatiPyPB/+ekO/M/q/7uvbt22kTt3zEnXPzCV13T3Gel4/6NduDu66xRvlPNkM1RjjxUdv+4WhGx6TftD19Q/dfzpwcHO+rE3fAAAAAElFTkSuQmCC"; + + const UserProfile = ({ stateCode="pg", userType, cityDetails }) => { + + const history = useHistory(); + const { t } = useTranslation(); + const url = window.location.href; + const stateId = Digit.ULBService.getStateId(); + const tenant = Digit.ULBService.getCurrentTenantId(); + const userInfo = Digit.UserService.getUser()?.info || {}; + const [userDetails, setUserDetails] = useState(null); + const [name, setName] = useState(userInfo?.name ? userInfo.name : ""); + const [email, setEmail] = useState(userInfo?.emailId ? userInfo.emailId : ""); + const [gender, setGender] = useState(userDetails?.gender); + const [city, setCity] = useState(userInfo?.permanentCity ? userInfo.permanentCity : cityDetails.name); + const [mobileNumber, setMobileNo] = useState(userInfo?.mobileNumber ? userInfo.mobileNumber : ""); + const [profilePic, setProfilePic] = useState(null); + const [profileImg, setProfileImg] = useState(""); + const [openUploadSlide, setOpenUploadSide] = useState(false); + const [changepassword, setChangepassword] = useState(false); + const [currentPassword, setCurrentPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [toast, setToast] = useState(null); + const [loading, setLoading] = useState(false); + const [windowWidth, setWindowWidth] = React.useState(window.innerWidth); + const [errors, setErrors] = React.useState({}); + const isMobile = window.Digit.Utils.browser.isMobile(); + + const getUserInfo = async () => { + const uuid = userInfo?.uuid; + if (uuid) { + const usersResponse = await Digit.UserService.userSearch(tenant, { uuid: [uuid] }, {}); + usersResponse && usersResponse.user && usersResponse.user.length && setUserDetails(usersResponse.user[0]); + } + }; + + React.useEffect(() => { + window.addEventListener("resize", () => setWindowWidth(window.innerWidth)); + return () => { + window.removeEventListener("resize", () => setWindowWidth(window.innerWidth)); + }; + }); + + useEffect(() => { + setLoading(true); + + getUserInfo(); + + setGender({ + i18nKey: undefined, + code: userDetails?.gender, + value: userDetails?.gender, + }); + + const thumbs = userDetails?.photo?.split(","); + setProfileImg(thumbs?.at(0)); + + setLoading(false); + }, [userDetails !== null]); + + let validation = {}; + const editScreen = false; // To-do: Deubug and make me dynamic or remove if not needed + const onClickAddPic = () => setOpenUploadSide(!openUploadSlide); + const TogleforPassword = () => setChangepassword(!changepassword); + const setGenderName = (value) => setGender(value); + const closeFileUploadDrawer = () => setOpenUploadSide(false); + + const setUserName = (value) => { + setName(value); + + if(!new RegExp(/^[a-zA-Z ]+$/i).test(value) || value.length === 0 || value.length > 50){ + setErrors({...errors, userName : {type: "pattern", message: "CORE_COMMON_PROFILE_NAME_INVALID"}}); + }else{ + setErrors({...errors, userName : null}) + } + } + + const setUserEmailAddress = (value) => { + if (userInfo?.userName !== value) { + setEmail(value); + + if (value.length && !(value.includes("@") && value.includes("."))) { + setErrors({ + ...errors, + emailAddress: { type: "pattern", message: "CORE_COMMON_PROFILE_EMAIL_INVALID" }, + }); + } else { + setErrors({ ...errors, emailAddress: null }); + } + } else { + setErrors({ ...errors, emailAddress: null }); + } + }; + + const setUserMobileNumber = (value) => { + setMobileNo(value); + + if (userType === "employee" && !new RegExp(/^[6-9]{1}[0-9]{9}$/).test(value)) { + setErrors({...errors, mobileNumber: {type: 'pattern', message: "CORE_COMMON_PROFILE_MOBILE_NUMBER_INVALID"}}) + }else{ + setErrors({...errors, mobileNumber: null}); + } + } + + const setUserCurrentPassword = (value) => { + setCurrentPassword(value); + + if (!new RegExp(/^([a-zA-Z0-9@#$%]{8,15})$/i).test(value)) { + setErrors({...errors, currentPassword: {type: "pattern", message: "CORE_COMMON_PROFILE_PASSWORD_INVALID"}}) + }else{ + setErrors({...errors, currentPassword: null}); + } + } + + const setUserNewPassword = (value) => { + setNewPassword(value); + + if (!new RegExp(/^([a-zA-Z0-9@#$%]{8,15})$/i).test(value)) { + setErrors({...errors, newPassword: {type: "pattern", message: "CORE_COMMON_PROFILE_PASSWORD_INVALID"}}) + }else{ + setErrors({...errors, newPassword: null}); + } + } + + const setUserConfirmPassword = (value) => { + setConfirmPassword(value); + + if (!new RegExp(/^([a-zA-Z0-9@#$%]{8,15})$/i).test(value)) { + setErrors({...errors, confirmPassword: {type: "pattern", message: "CORE_COMMON_PROFILE_PASSWORD_INVALID"}}) + }else{ + setErrors({...errors, confirmPassword: null}); + } + } + + const removeProfilePic = () => { + setProfilePic(null); + setProfileImg(null); + }; + + const showToast = (type, message, duration = 5000) => { + setToast({ key: type, action: message }); + setTimeout(() => { + setToast(null); + }, duration); + }; + + const updateProfile = async () => { + setLoading(true); + try { + const requestData = { + ...userInfo, + name, + gender: gender?.value, + emailId: email, + photo: profilePic, + }; + + if (!new RegExp(/^([a-zA-Z ])*$/).test(name) || name === "" || name.length > 50 || name.length < 1) { + throw JSON.stringify({ type: "error", message: t("CORE_COMMON_PROFILE_NAME_INVALID") }); + } + + if (userType === "employee" && !new RegExp(/^[6-9]{1}[0-9]{9}$/).test(mobileNumber)) { + throw JSON.stringify({ type: "error", message: t("CORE_COMMON_PROFILE_MOBILE_NUMBER_INVALID") }); + } + + if (email.length && !(email.includes("@") && email.includes("."))) { + throw JSON.stringify({ type: "error", message: t("CORE_COMMON_PROFILE_EMAIL_INVALID") }); + } + + if (changepassword && (currentPassword.length || newPassword.length || confirmPassword.length)) { + if (newPassword !== confirmPassword) { + throw JSON.stringify({ type: "error", message: t("CORE_COMMON_PROFILE_PASSWORD_MISMATCH") }); + } + + if (!(currentPassword.length && newPassword.length && confirmPassword.length)) { + throw JSON.stringify({ type: "error", message: t("CORE_COMMON_PROFILE_PASSWORD_INVALID") }); + } + + if (!new RegExp(/^([a-zA-Z0-9@#$%]{8,15})$/i).test(newPassword) && !new RegExp(/^([a-zA-Z0-9@#$%]{8,15})$/i).test(confirmPassword)) { + throw JSON.stringify({ type: "error", message: t("CORE_COMMON_PROFILE_PASSWORD_INVALID") }); + } + } + + const { responseInfo, user } = await Digit.UserService.updateUser(requestData, stateCode); + + if (responseInfo && responseInfo.status === "200") { + const user = Digit.UserService.getUser(); + + if (user) { + Digit.UserService.setUser({ + ...user, + info: { + ...user.info, + name, + mobileNumber, + emailId: email, + permanentCity: city, + }, + }); + } + } + + if (currentPassword.length && newPassword.length && confirmPassword.length) { + const requestData = { + existingPassword: currentPassword, + newPassword: newPassword, + tenantId: tenant, + type: "EMPLOYEE", + username: userInfo?.userName, + confirmPassword: confirmPassword, + }; + + if (newPassword === confirmPassword) { + try { + const res = await Digit.UserService.changePassword(requestData, tenant); + + const { responseInfo: changePasswordResponseInfo } = res; + if (changePasswordResponseInfo?.status && changePasswordResponseInfo.status === "200") { + showToast("success", t("CORE_COMMON_PROFILE_UPDATE_SUCCESS_WITH_PASSWORD"), 5000); + setTimeout(() => Digit.UserService.logout(), 2000); + } else { + throw ""; + } + } catch (error) { + throw JSON.stringify({ + type: "error", + message: error.Errors?.at(0)?.description ? error.Errors.at(0).description : "CORE_COMMON_PROFILE_UPDATE_ERROR_WITH_PASSWORD", + }); + } + } else { + throw JSON.stringify({ type: "error", message: "CORE_COMMON_PROFILE_ERROR_PASSWORD_NOT_MATCH" }); + } + } else if (responseInfo?.status && responseInfo.status === "200") { + showToast("success", t("CORE_COMMON_PROFILE_UPDATE_SUCCESS"), 5000); + } + } catch (error) { + const errorObj = JSON.parse(error); + showToast(errorObj.type, t(errorObj.message), 5000); + } + + setLoading(false); + }; + + let menu = []; + const { data: Menu } = Digit.Hooks.useGenderMDMS(stateId, "common-masters", "GenderType"); + Menu && + Menu.map((genderDetails) => { + menu.push({ i18nKey: `PT_COMMON_GENDER_${genderDetails.code}`, code: `${genderDetails.code}`, value: `${genderDetails.code}` }); + }); + + const setFileStoreId = async (fileStoreId) => { + setProfilePic(fileStoreId); + + const thumbnails = fileStoreId ? await getThumbnails([fileStoreId], stateId) : null; + + setProfileImg(thumbnails?.thumbs[0]); + + closeFileUploadDrawer(); + }; + + const getThumbnails = async (ids, tenantId) => { + const res = await Digit.UploadServices.Filefetch(ids, tenantId); + if (res.data.fileStoreIds && res.data.fileStoreIds.length !== 0) { + return { + thumbs: res.data.fileStoreIds.map((o) => o.url.split(",")[3]), + images: res.data.fileStoreIds.map((o) => Digit.Utils.getFileUrl(o.url)), + }; + } else { + return null; + } + }; + + if (loading) return ; + + return ( +
+
+ {userType === "citizen" ? ( + + ) : ( + + )} +
+
+
+
+ + +
+
+
+ {userType === "citizen" ? ( + + + {`${t("CORE_COMMON_PROFILE_NAME")}`}* +
+ setUserName(e.target.value)} + {...(validation = { + isRequired: true, + pattern: "^[a-zA-Z-.`' ]*$", + type: "tel", + title: t("CORE_COMMON_PROFILE_NAME_ERROR_MESSAGE"), + })} + disable={editScreen} + /> + {errors?.userName && {t(errors?.userName?.message)} } +
+
+ + + {`${t("CORE_COMMON_PROFILE_GENDER")}`} + + + + + {`${t("CORE_COMMON_PROFILE_EMAIL")}`} +
+ setUserEmailAddress(e.target.value)} + disable={editScreen} + /> + {errors?.emailAddress && {t(errors?.emailAddress?.message)} } +
+
+ + +
+ ) : ( + + + + {`${t("CORE_COMMON_PROFILE_NAME")}`}* + +
+ setUserName(e.target.value)} + placeholder="Enter Your Name" + {...(validation = { + isRequired: true, + pattern: "^[a-zA-Z-.`' ]*$", + type: "text", + title: t("CORE_COMMON_PROFILE_NAME_ERROR_MESSAGE"), + })} + disable={editScreen} + /> + {errors?.userName && {t(errors?.userName?.message)} } +
+
+ + + {`${t( + "CORE_COMMON_PROFILE_GENDER" + )}`} + + + + + {`${t( + "CORE_COMMON_PROFILE_CITY" + )}`} +
+ setCity(e.target.value)} + placeholder="Enter Your City Name" + {...(validation = { + isRequired: true, + // pattern: "^[a-zA-Z-.`' ]*$", + type: "text", + title: t("CORE_COMMON_PROFILE_CITY_ERROR_MESSAGE"), + })} + disable={true} + /> + +
+
+ + + {`${t("CORE_COMMON_PROFILE_MOBILE_NUMBER")}*`} +
+ setUserMobileNumber(value)} + disable={true} + {...{ required: true, pattern: "[6-9]{1}[0-9]{9}", type: "tel", title: t("CORE_COMMON_PROFILE_MOBILE_NUMBER_INVALID") }} + /> + {errors?.mobileNumber && {t(errors?.mobileNumber?.message)} } +
+
+ + + {`${t( + "CORE_COMMON_PROFILE_EMAIL" + )}`} +
+ setUserEmailAddress(e.target.value)} + disable={editScreen} + /> + {errors?.emailAddress && {t(errors?.emailAddress?.message)} } +
+
+ + +
+ + {t("CORE_COMMON_CHANGE_PASSWORD")} + + {changepassword ? ( +
+ + {`${t( + "CORE_COMMON_PROFILE_CURRENT_PASSWORD" + )}`} +
+ setUserCurrentPassword(e.target.value)} + disable={editScreen} + /> + {errors?.currentPassword && {t(errors?.currentPassword?.message)}} +
+
+ + + {`${t( + "CORE_COMMON_PROFILE_NEW_PASSWORD" + )}`} +
+ setUserNewPassword(e.target.value)} + disable={editScreen} + /> + {errors?.newPassword && {t(errors?.newPassword?.message)}} +
+
+ + + {`${t( + "CORE_COMMON_PROFILE_CONFIRM_PASSWORD" + )}`} +
+ setUserConfirmPassword(e.target.value)} + disable={editScreen} + /> + {errors?.confirmPassword && {t(errors?.confirmPassword?.message)}} +
+
+
+ ) : ( + "" + )} +
+
+
+ )} +
+
+ + {userType === "employee" ? ( +
+ + + {/* */} + +
+ ) : ( + "" + )} + {toast && ( + setToast(null)} + style={{ maxWidth: "670px" }} + /> + )} + + {openUploadSlide == true ? ( + + ) : ( + "" + )} +
+ ); + }; + + export default UserProfile; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Home/index.js b/micro-ui/web/packages/ui/src/pages/citizen/Home/index.js new file mode 100644 index 00000000000..280a4006615 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Home/index.js @@ -0,0 +1,173 @@ +import { + Calender, CardBasedOptions, CaseIcon, ComplaintIcon, DocumentIcon, HomeIcon, Loader, OBPSIcon, PTIcon, WhatsNewCard + } from "@digit-ui/digit-ui-react-components"; + import React from "react"; + import { useTranslation } from "react-i18next"; + import { useHistory } from "react-router-dom"; + + const Home = () => { + const { t } = useTranslation(); + const history = useHistory(); + const tenantId = Digit.ULBService.getCitizenCurrentTenant(true); + const { data: { stateInfo, uiHomePage } = {}, isLoading } = Digit.Hooks.useStore.getInitData(); + let isMobile = window.Digit.Utils.browser.isMobile(); + + const conditionsToDisableNotificationCountTrigger = () => { + if (Digit.UserService?.getUser()?.info?.type === "EMPLOYEE") return false; + if (!Digit.UserService?.getUser()?.access_token) return false; + return true; + }; + + const { data: EventsData, isLoading: EventsDataLoading } = Digit.Hooks.useEvents({ + tenantId, + variant: "whats-new", + config: { + enabled: conditionsToDisableNotificationCountTrigger(), + }, + }); + + if (!tenantId) { + history.push(`/${window?.contextPath}/citizen/select-language`); + } + + const appBannerWebObj = uiHomePage?.appBannerDesktop; + const appBannerMobObj = uiHomePage?.appBannerMobile; + const citizenServicesObj = uiHomePage?.citizenServicesCard; + const infoAndUpdatesObj = uiHomePage?.informationAndUpdatesCard; + const whatsAppBannerWebObj = uiHomePage?.whatsAppBannerDesktop; + const whatsAppBannerMobObj = uiHomePage?.whatsAppBannerMobile; + const whatsNewSectionObj = uiHomePage?.whatsNewSection; + const redirectURL = uiHomePage?.redirectURL; + /* configure redirect URL only if it is required to overide the default citizen home screen */ + if (redirectURL) { + history.push(`/${window?.contextPath}/citizen/${redirectURL}`); + } + /* fix for sanitation ui */ + if (window?.location?.href?.includes?.("sanitation-ui")) { + history.push(`/${window?.contextPath}/citizen/all-services`); + } + + const handleClickOnWhatsAppBanner = (obj) => { + window.open(obj?.navigationUrl); + }; + + const allCitizenServicesProps = { + header: t(citizenServicesObj?.headerLabel), + sideOption: { + name: t(citizenServicesObj?.sideOption?.name), + onClick: () => history.push(citizenServicesObj?.sideOption?.navigationUrl), + }, + options: [ + { + name: t(citizenServicesObj?.props?.[0]?.label), + Icon: , + onClick: () => history.push(citizenServicesObj?.props?.[0]?.navigationUrl), + }, + { + name: t(citizenServicesObj?.props?.[1]?.label), + Icon: , + onClick: () => history.push(citizenServicesObj?.props?.[1]?.navigationUrl), + }, + { + name: t(citizenServicesObj?.props?.[2]?.label), + Icon: , + onClick: () => history.push(citizenServicesObj?.props?.[2]?.navigationUrl), + }, + // { + // name: t("ACTION_TEST_WATER_AND_SEWERAGE"), + // Icon: , + // onClick: () => history.push(`/${window?.contextPath}/citizen`) + // }, + { + name: t(citizenServicesObj?.props?.[3]?.label), + Icon: , + onClick: () => history.push(citizenServicesObj?.props?.[3]?.navigationUrl), + }, + ], + styles: { display: "flex", flexWrap: "wrap", justifyContent: "flex-start", width: "100%" }, + }; + const allInfoAndUpdatesProps = { + header: t(infoAndUpdatesObj?.headerLabel), + sideOption: { + name: t(infoAndUpdatesObj?.sideOption?.name), + onClick: () => history.push(infoAndUpdatesObj?.sideOption?.navigationUrl), + }, + options: [ + { + name: t(infoAndUpdatesObj?.props?.[0]?.label), + Icon: , + onClick: () => history.push(infoAndUpdatesObj?.props?.[0]?.navigationUrl), + }, + { + name: t(infoAndUpdatesObj?.props?.[1]?.label), + Icon: , + onClick: () => history.push(infoAndUpdatesObj?.props?.[1]?.navigationUrl), + }, + { + name: t(infoAndUpdatesObj?.props?.[2]?.label), + Icon: , + onClick: () => history.push(infoAndUpdatesObj?.props?.[2]?.navigationUrl), + }, + { + name: t(infoAndUpdatesObj?.props?.[3]?.label), + Icon: , + onClick: () => history.push(infoAndUpdatesObj?.props?.[3]?.navigationUrl), + }, + // { + // name: t("CS_COMMON_HELP"), + // Icon: + // } + ], + styles: { display: "flex", flexWrap: "wrap", justifyContent: "flex-start", width: "100%" }, + }; + + return isLoading ? ( + + ) : ( +
+ {/*
+ +
*/} +
+ { +
+ {isMobile ? : } + {/*
+ +
*/} +
+ + +
+
+ } + + {(whatsAppBannerMobObj || whatsAppBannerWebObj) && ( +
+ {isMobile ? ( + handleClickOnWhatsAppBanner(whatsAppBannerMobObj)} /> + ) : ( + handleClickOnWhatsAppBanner(whatsAppBannerWebObj)} /> + )} +
+ )} + + {conditionsToDisableNotificationCountTrigger() ? ( + EventsDataLoading ? ( + + ) : ( +
+
+

{t(whatsNewSectionObj?.headerLabel)}

+

history.push(whatsNewSectionObj?.sideOption?.navigationUrl)}>{t(whatsNewSectionObj?.sideOption?.name)}

+
+ +
+ ) + ) : null} +
+
+ ); + }; + + export default Home; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/HowItWorks/howItWorks.js b/micro-ui/web/packages/ui/src/pages/citizen/HowItWorks/howItWorks.js new file mode 100644 index 00000000000..b25e04822ad --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/HowItWorks/howItWorks.js @@ -0,0 +1,123 @@ +import { + BackButton, CloseSvg, CustomButton, DownloadImgIcon, Header, Loader, PDFSvg + } from "@digit-ui/digit-ui-react-components"; + import React, { Fragment, useState } from "react"; + import { useTranslation } from "react-i18next"; + + const HowItWorks = ({ module }) => { + const user = Digit.UserService.getUser(); + const tenantId = user?.info?.tenantId || Digit.ULBService.getCurrentTenantId(); + const { t } = useTranslation(); + const storeData = Digit.SessionStorage.get("initData"); + const stateInfo = storeData.stateInfo; + const selectedLanguage = Digit.StoreData.getCurrentLanguage(); + const [selected, setselected] = useState(selectedLanguage); + const handleChangeLanguage = (language) => { + setselected(language.value); + Digit.LocalizationService.changeLanguage(language.value, stateInfo.code); + }; + const [videoPlay, setVideoPlay] = useState(false); + const [vidSrc, setVidSrc] = useState(""); + + const ViDSvg = () => ( + + + + ); + const onClickVideo = (vidObj) => { + if (selected === "hi_IN") { + setVidSrc(vidObj["hi_IN"]); + } else { + setVidSrc(vidObj["en_IN"]); + } + setVideoPlay(true); + }; + const onClose = () => { + setVideoPlay(false); + }; + + const { isLoading, data } = Digit.Hooks.useGetHowItWorksJSON(Digit.ULBService.getStateId()); + + const mdmsConfigResult = data?.MdmsRes["common-masters"]?.howItWorks[0]?.[`${module}`]; + const languages = [ + { + label: "ENGLISH", + value: "en_IN", + }, + { + label: "हिंदी", + value: "hi_IN", + }, + ]; + + if (isLoading) { + return ; + } + return ( + +
+ +
+
{t("HOW_IT_WORKS")}
+
+
+ {languages.map((language, index) => ( +
+ handleChangeLanguage(language)} + > +
+ ))} +
+ {mdmsConfigResult.videosJson.map((videos, index) => ( +
+
+
onClickVideo(videos)}> +
+ +
+
+
+

{t(videos.headerLabel)}

+

{t(videos.description)}

+
+
+
+ ))} +
+
+
+
+ +
+
+

{t(mdmsConfigResult.pdfHeader)}

+

{t(mdmsConfigResult.pdfDesc)}

+
+
+
+ +
+
+
+ {videoPlay && ( +
+
+ +
+ +
+ )} +
+
+ ); + }; + + export default HowItWorks; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectMobileNumber.js b/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectMobileNumber.js new file mode 100644 index 00000000000..fc402772919 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectMobileNumber.js @@ -0,0 +1,18 @@ +import { FormStep } from "@digit-ui/digit-ui-react-components"; +import React from "react"; + +const SelectMobileNumber = ({ t, onSelect, showRegisterLink, mobileNumber, onMobileChange, config, canSubmit }) => { + return ( + + ); +}; + +export default SelectMobileNumber; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectName.js b/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectName.js new file mode 100644 index 00000000000..524674a213a --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectName.js @@ -0,0 +1,8 @@ +import { FormStep } from "@digit-ui/digit-ui-react-components"; +import React from "react"; + +const SelectName = ({ config, onSelect, t, isDisabled }) => { + return ; +}; + +export default SelectName; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectOtp.js b/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectOtp.js new file mode 100644 index 00000000000..f03e0b139e6 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Login/SelectOtp.js @@ -0,0 +1,51 @@ +import { CardLabelError, CardText, FormStep, OTPInput } from "@digit-ui/digit-ui-react-components"; +import React, { Fragment, useState } from "react"; +import useInterval from "../../../hooks/useInterval"; + +const SelectOtp = ({ config, otp, onOtpChange, onResend, onSelect, t, error, userType = "citizen", canSubmit }) => { + const [timeLeft, setTimeLeft] = useState(30); + + useInterval( + () => { + setTimeLeft(timeLeft - 1); + }, + timeLeft > 0 ? 1000 : null + ); + + const handleResendOtp = () => { + onResend(); + setTimeLeft(2); + }; + + if (userType === "employee") { + return ( + + + {timeLeft > 0 ? ( + {`${t("CS_RESEND_ANOTHER_OTP")} ${timeLeft} ${t("CS_RESEND_SECONDS")}`} + ) : ( +

+ {t("CS_RESEND_OTP")} +

+ )} + {!error && {t("CS_INVALID_OTP")}} +
+ ); + } + + return ( + + + {timeLeft > 0 ? ( + {`${t("CS_RESEND_ANOTHER_OTP")} ${timeLeft} ${t("CS_RESEND_SECONDS")}`} + ) : ( +

+ {t("CS_RESEND_OTP")} +

+ )} + {!error && {t("CS_INVALID_OTP")}} +
+ ); +}; + +export default SelectOtp; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Login/config.js b/micro-ui/web/packages/ui/src/pages/citizen/Login/config.js new file mode 100644 index 00000000000..1f8e675d288 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Login/config.js @@ -0,0 +1,52 @@ +export const loginSteps = [ + { + texts: { + header: "CS_LOGIN_PROVIDE_MOBILE_NUMBER", + cardText: "CS_LOGIN_TEXT", + nextText: "CS_COMMONS_NEXT", + submitBarLabel: "CS_COMMONS_NEXT", + }, + inputs: [ + { + label: "CORE_COMMON_MOBILE_NUMBER", + type: "text", + name: "mobileNumber", + error: "ERR_HRMS_INVALID_MOB_NO", + validation: { + required: true, + minLength: 10, + maxLength: 10, + }, + }, + ], + }, + { + texts: { + header: "CS_LOGIN_OTP", + cardText: "CS_LOGIN_OTP_TEXT", + nextText: "CS_COMMONS_NEXT", + submitBarLabel: "CS_COMMONS_NEXT", + }, + }, + { + texts: { + header: "CS_LOGIN_PROVIDE_NAME", + cardText: "CS_LOGIN_NAME_TEXT", + nextText: "CS_COMMONS_NEXT", + submitBarLabel: "CS_COMMONS_NEXT", + }, + inputs: [ + { + label: "CORE_COMMON_NAME", + type: "text", + name: "name", + error: "CORE_COMMON_NAME_VALIDMSG", + validation: { + required: true, + minLength: 1, + pattern: /^[^{0-9}^\$\"<>?\\\\~!@#$%^()+={}\[\]*,/_:;“”‘’]{1,50}$/i, + }, + }, + ], + }, + ]; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/Login/index.js b/micro-ui/web/packages/ui/src/pages/citizen/Login/index.js new file mode 100644 index 00000000000..2ebc275e52c --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/Login/index.js @@ -0,0 +1,274 @@ +import { AppContainer, BackButton, Toast } from "@digit-ui/digit-ui-react-components"; +import React, { useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Route, Switch, useHistory, useLocation, useRouteMatch } from "react-router-dom"; +import { loginSteps } from "./config"; +import SelectMobileNumber from "./SelectMobileNumber"; +import SelectName from "./SelectName"; +import SelectOtp from "./SelectOtp"; + +const TYPE_REGISTER = { type: "register" }; +const TYPE_LOGIN = { type: "login" }; +const DEFAULT_USER = "digit-user"; +const DEFAULT_REDIRECT_URL = `/${window?.contextPath}/citizen`; + +/* set citizen details to enable backward compatiable */ +const setCitizenDetail = (userObject, token, tenantId) => { + let locale = JSON.parse(sessionStorage.getItem("Digit.initData"))?.value?.selectedLanguage; + localStorage.setItem("Citizen.tenant-id", tenantId); + localStorage.setItem("tenant-id", tenantId); + localStorage.setItem("citizen.userRequestObject", JSON.stringify(userObject)); + localStorage.setItem("locale", locale); + localStorage.setItem("Citizen.locale", locale); + localStorage.setItem("token", token); + localStorage.setItem("Citizen.token", token); + localStorage.setItem("user-info", JSON.stringify(userObject)); + localStorage.setItem("Citizen.user-info", JSON.stringify(userObject)); +}; + +const getFromLocation = (state, searchParams) => { + return state?.from || searchParams?.from || DEFAULT_REDIRECT_URL; +}; + +const Login = ({ stateCode="pg", isUserRegistered = true }) => { + + const { t } = useTranslation(); + const location = useLocation(); + const { path, url } = useRouteMatch(); + const history = useHistory(); + const [user, setUser] = useState(null); + const [error, setError] = useState(null); + const [isOtpValid, setIsOtpValid] = useState(true); + const [tokens, setTokens] = useState(null); + const [params, setParmas] = useState(isUserRegistered ? {} : location?.state?.data); + const [errorTO, setErrorTO] = useState(null); + const searchParams = Digit.Hooks.useQueryParams(); + const [canSubmitName, setCanSubmitName] = useState(false); + const [canSubmitOtp, setCanSubmitOtp] = useState(true); + const [canSubmitNo, setCanSubmitNo] = useState(true); + + useEffect(() => { + let errorTimeout; + if (error) { + if (errorTO) { + clearTimeout(errorTO); + setErrorTO(null); + } + errorTimeout = setTimeout(() => { + setError(""); + }, 5000); + setErrorTO(errorTimeout); + } + return () => { + errorTimeout && clearTimeout(errorTimeout); + }; + }, [error]); + + useEffect(() => { + if (!user) { + return; + } + Digit.SessionStorage.set("citizen.userRequestObject", user); + Digit.UserService.setUser(user); + setCitizenDetail(user?.info, user?.access_token, stateCode); + + + const redirectPath = window.contextPath ? `/${window.contextPath}/citizen` : DEFAULT_REDIRECT_URL; + + if (!Digit.ULBService.getCitizenCurrentTenant(true)) { + history.replace(`/${window?.contextPath}/citizen/select-location`, { + redirectBackTo: redirectPath, + }); + } else { + history.replace(redirectPath); + } + }, [user]); + + const stepItems = useMemo(() => + loginSteps.map( + (step) => { + const texts = {}; + for (const key in step.texts) { + texts[key] = t(step.texts[key]); + } + return { ...step, texts }; + }, + [loginSteps] + ) + ); + + const getUserType = () => Digit.UserService.getType(); + + const handleOtpChange = (otp) => { + setParmas({ ...params, otp }); + }; + + const handleMobileChange = (event) => { + const { value } = event.target; + setParmas({ ...params, mobileNumber: value }); + }; + + const selectMobileNumber = async (mobileNumber) => { + setCanSubmitNo(false); + setParmas({ ...params, ...mobileNumber }); + const data = { + ...mobileNumber, + tenantId: stateCode, + userType: getUserType(), + }; + if (isUserRegistered) { + const [res, err] = await sendOtp({ otp: { ...data, ...TYPE_LOGIN } }); + if (!err) { + setCanSubmitNo(true); + history.replace(`${path}/otp`, { from: getFromLocation(location.state, searchParams), role: location.state?.role }); + return; + } else { + setCanSubmitNo(true); + if (!(location.state && location.state.role === "FSM_DSO")) { + history.push(`/${window?.contextPath}/citizen/register/name`, { from: getFromLocation(location.state, searchParams), data: data }); + } + } + if (location.state?.role) { + setCanSubmitNo(true); + setError(location.state?.role === "FSM_DSO" ? t("ES_ERROR_DSO_LOGIN") : "User not registered."); + } + } else { + const [res, err] = await sendOtp({ otp: { ...data, ...TYPE_REGISTER } }); + if (!err) { + setCanSubmitNo(true); + history.replace(`${path}/otp`, { from: getFromLocation(location.state, searchParams) }); + return; + } + setCanSubmitNo(true); + } + }; + + const selectName = async (name) => { + const data = { + ...params, + tenantId: stateCode, + userType: getUserType(), + ...name, + }; + setParmas({ ...params, ...name }); + setCanSubmitName(true); + const [res, err] = await sendOtp({ otp: { ...data, ...TYPE_REGISTER } }); + if (res) { + setCanSubmitName(false); + history.replace(`${path}/otp`, { from: getFromLocation(location.state, searchParams) }); + } else { + setCanSubmitName(false); + } + }; + + const selectOtp = async () => { + try { + setIsOtpValid(true); + setCanSubmitOtp(false); + const { mobileNumber, otp, name } = params; + if (isUserRegistered) { + const requestData = { + username: mobileNumber, + password: otp, + tenantId: stateCode, + userType: getUserType(), + }; + const { ResponseInfo, UserRequest: info, ...tokens } = await Digit.UserService.authenticate(requestData); + + if (location.state?.role) { + const roleInfo = info.roles.find((userRole) => userRole.code === location.state.role); + if (!roleInfo || !roleInfo.code) { + setError(t("ES_ERROR_USER_NOT_PERMITTED")); + setTimeout(() => history.replace(DEFAULT_REDIRECT_URL), 5000); + return; + } + } + if (window?.globalConfigs?.getConfig("ENABLE_SINGLEINSTANCE")) { + info.tenantId = Digit.ULBService.getStateId(); + } + + setUser({ info, ...tokens }); + } else if (!isUserRegistered) { + const requestData = { + name, + username: mobileNumber, + otpReference: otp, + tenantId: stateCode, + }; + + const { ResponseInfo, UserRequest: info, ...tokens } = await Digit.UserService.registerUser(requestData, stateCode); + + if (window?.globalConfigs?.getConfig("ENABLE_SINGLEINSTANCE")) { + info.tenantId = Digit.ULBService.getStateId(); + } + + setUser({ info, ...tokens }); + } + } catch (err) { + setCanSubmitOtp(true); + setIsOtpValid(false); + } + }; + + const resendOtp = async () => { + const { mobileNumber } = params; + const data = { + mobileNumber, + tenantId: stateCode, + userType: getUserType(), + }; + if (!isUserRegistered) { + const [res, err] = await sendOtp({ otp: { ...data, ...TYPE_REGISTER } }); + } else if (isUserRegistered) { + const [res, err] = await sendOtp({ otp: { ...data, ...TYPE_LOGIN } }); + } + }; + + const sendOtp = async (data) => { + try { + const res = await Digit.UserService.sendOtp(data, stateCode); + return [res, null]; + } catch (err) { + return [null, err]; + } + }; + + return ( +
+ + + + + + + + + + + + + {error && setError(null)} />} + + +
+ ); +}; + +export default Login; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/SearchApp.js b/micro-ui/web/packages/ui/src/pages/citizen/SearchApp.js new file mode 100644 index 00000000000..d02c3361c58 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/SearchApp.js @@ -0,0 +1,88 @@ +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import AuditSearchApplication from "../../components/Search"; +const Search = ({ path }) => { + const { t } = useTranslation(); + const tenantId = Digit.ULBService.getCitizenCurrentTenant() + const [payload, setPayload] = useState({}); + const convertDateToEpoch = (dateString, dayStartOrEnd = "dayend") => { + //example input format : "2018-10-02" + try { + const parts = dateString.match(/(\d{4})-(\d{1,2})-(\d{1,2})/); + const DateObj = new Date(Date.UTC(parts[1], parts[2] - 1, parts[3])); + DateObj.setMinutes(DateObj.getMinutes() + DateObj.getTimezoneOffset()); + if (dayStartOrEnd === "dayend") { + DateObj.setHours(DateObj.getHours() + 24); + DateObj.setSeconds(DateObj.getSeconds() - 1); + } + return DateObj.getTime(); + } catch (e) { + return dateString; + } + }; + function onSubmit(_data) { + Digit.SessionStorage.set("AUDIT_APPLICATION_DETAIL", { + offset: 0, + limit: 5, + sortBy: "commencementDate", + sortOrder: "DESC", + }); + const data = { + ..._data, + fromDate: convertDateToEpoch(_data?.fromDate), + toDate: convertDateToEpoch(_data?.toDate), + }; + + setPayload( + Object.keys(data) + .filter((k) => data[k]) + .reduce((acc, key) => ({ ...acc, [key]: typeof data[key] === "object" ? data[key] : data[key] }), {}) + ); + } + useEffect(() => { + const storedPayload = Digit.SessionStorage.get("AUDIT_APPLICATION_DETAIL") || {}; + if (storedPayload) { + const data = { + ...storedPayload, + }; + + setPayload( + Object.keys(data) + .filter((k) => data[k]) + .reduce((acc, key) => ({ ...acc, [key]: typeof data[key] === "object" ? data[key].code : data[key] }), {}) + ); + } + }, []); + const config = { + enabled: !!(payload && Object.keys(payload).length > 0), + }; + const newObj = { ...payload }; + + const { + isLoading, + data, +} = Digit.Hooks.useAudit({ + tenantId, + filters: { + ...newObj, + }, + config, + }); + + return ( + 0 + ? data?.ElasticSearchData + : { display: "ES_COMMON_NO_DATA" } + : "" + } + count={data?.ElasticSearchData?.length} + /> + ); + }; +export default Search; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/StaticDynamicComponent/StaticDynamicCard.js b/micro-ui/web/packages/ui/src/pages/citizen/StaticDynamicComponent/StaticDynamicCard.js new file mode 100644 index 00000000000..462bea805d4 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/StaticDynamicComponent/StaticDynamicCard.js @@ -0,0 +1,269 @@ +import { + Card, + CaseIcon, ComplaintIcon, HelpLineIcon, Loader, MCollectIcon, PTIcon, RupeeSymbol, ServiceCenterIcon, TimerIcon, ValidityTimeIcon, + WhatsappIconGreen + } from "@digit-ui/digit-ui-react-components"; + import React from "react"; + import { useTranslation } from "react-i18next"; + + const StaticDynamicCard = ({ moduleCode }) => { + const { t } = useTranslation(); + const tenantId = Digit.ULBService.getCitizenCurrentTenant(); + const { isLoading: isMdmsLoading, data: mdmsData } = Digit.Hooks.useStaticData(Digit.ULBService.getStateId()); + const { isLoading: isSearchLoading, error, data: dynamicData, isSuccess } = Digit.Hooks.useDynamicData({ + moduleCode, + tenantId: tenantId, + filters: {}, + t, + }); + const handleClickOnWhatsApp = (obj) => { + window.open(obj); + }; + + const IconComponent = ({ module, styles }) => { + switch (module) { + case "TL": + return ; + case "PT": + return ; + case "MCOLLECT": + return ; + case "PGR": + return ; + default: + return ; + } + }; + const mdmsConfigResult = mdmsData?.MdmsRes["common-masters"]?.StaticData[0]?.[`${moduleCode}`]; + + const StaticDataIconComponentOne = ({ module }) => { + switch (module) { + case "PT": + case "WS": + return ( + + + + ); + default: + return null; + } + }; + const StaticDataIconComponentTwo = ({ module }) => { + switch (module) { + case "PT": + return ( + + + + ); + case "WS": + return ( + + + + ); + default: + return null; + } + }; + const staticContent = (module) => { + switch (module) { + case "TL": + case "PT": + case "MCOLLECT": + return { + staticCommonContent: t("COMMON_VALIDITY"), + validity: mdmsConfigResult?.validity + (mdmsConfigResult?.validity === "1" ? t("COMMON_DAY") : t("COMMON_DAYS")), + }; + case "PGR": + return { + staticCommonContent: t("ACTION_TEST_COMPLAINT_TYPES"), + }; + case "OBPS": + return { + staticCommonContent: t("BUILDING_PLAN_PERMIT_VALIDITY"), + validity: mdmsConfigResult?.validity + " " + (mdmsConfigResult?.validity === "1" ? t("COMMON_DAY") : t("COMMON_DAYS")), + }; + default: + return { + staticCommonContent: "", + }; + } + }; + + const staticData = (module) => { + switch (module) { + case "PT": + return { + staticDataOne: mdmsConfigResult?.staticDataOne + " " + t("COMMON_DAYS"), + staticDataOneHeader: t("APPLICATION_PROCESSING_TIME"), + staticDataTwo: mdmsConfigResult?.staticDataTwo, + staticDataTwoHeader: t("APPLICATION_PROCESSING_FEE"), + }; + case "WS": + return { + staticDataOne: "", + staticDataOneHeader: + t("PAY_WATER_CHARGES_BY") + " " + mdmsConfigResult?.staticDataOne + " " + t("COMMON_DAYS") + " " + t("OF_BILL_GEN_TO_AVOID_LATE_FEE"), + staticDataTwo: mdmsConfigResult?.staticDataTwo + " " + t("COMMON_DAYS"), + staticDataTwoHeader: t("APPLICATION_PROCESSING_TIME"), + }; + default: + return {}; + } + }; + + if (isMdmsLoading || isSearchLoading) { + return ; + } + return mdmsConfigResult ? ( + + {mdmsConfigResult && mdmsConfigResult?.payViaWhatsApp ? ( + +
handleClickOnWhatsApp(mdmsConfigResult?.payViaWhatsApp)}> +
{t("PAY_VIA_WHATSAPP")}
+
+ +
+
+
+ ) : null} + {mdmsConfigResult && mdmsConfigResult?.helpline ? ( + +
+
{t("CALL_CENTER_HELPLINE")}
+
+ +
+
+
+ {mdmsConfigResult?.helpline?.contactOne ? ( + + ) : null} + {mdmsConfigResult?.helpline?.contactTwo ? ( + + ) : null} +
+
+ ) : null} + {mdmsConfigResult && mdmsConfigResult?.serviceCenter ? ( + +
+
{t("CITIZEN_SERVICE_CENTER")}
+
+ +
+
+
+
{mdmsConfigResult?.serviceCenter}
+
+ {mdmsConfigResult?.viewMapLocation ? ( + + ) : null} +
+ ) : ( +
+ )} + + {error || dynamicData == null || dynamicData?.dynamicDataOne === null ? ( +
+ ) : ( +
+
+ + {dynamicData?.dynamicDataOne} +
+
+ )} + {error || dynamicData == null || dynamicData?.dynamicDataTwo === null ? ( +
+ ) : ( +
+
+ + {dynamicData?.dynamicDataTwo} +
+
+ )} + {mdmsConfigResult && mdmsConfigResult?.staticDataOne ? ( +
+
+ + + + {staticData(moduleCode)?.staticDataOneHeader} + + {`${staticData(moduleCode)?.staticDataOne}`} + +
+
+ ) : ( +
+ )} + {mdmsConfigResult && mdmsConfigResult?.staticDataTwo ? ( +
+
+ + + {staticData(moduleCode)?.staticDataTwoHeader} + {staticData(moduleCode)?.staticDataTwo} + +
+
+ ) : ( +
+ )} + {mdmsConfigResult && mdmsConfigResult?.validity ? ( +
+
+ + + + + {staticContent(moduleCode)?.staticCommonContent} + {staticContent(moduleCode)?.validity} + +
+
+ ) : ( +
+ )} + {error || dynamicData == null || !dynamicData?.staticData || dynamicData?.staticData === null ? ( +
+ ) : ( +
+
+ {moduleCode === "PGR" ? ( + + ) : ( + + + + )} + + {staticContent(moduleCode)?.staticCommonContent} + {dynamicData?.staticData} + +
+
+ )} + + + ) : ( + + ); + }; + + export default StaticDynamicCard; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/citizen/index.js b/micro-ui/web/packages/ui/src/pages/citizen/index.js new file mode 100644 index 00000000000..57c3cdf9902 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/citizen/index.js @@ -0,0 +1,228 @@ +import { BackButton, CitizenHomeCard, CitizenInfoLabel } from "@digit-ui/digit-ui-react-components"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Route, Switch, useHistory, useRouteMatch } from "react-router-dom"; +import ErrorBoundary from "../../components/ErrorBoundaries"; +import ErrorComponent from "../../components/ErrorComponent"; +import { AppHome, processLinkData } from "../../components/Home"; +import TopBarSideBar from "../../components/TopBarSideBar"; +import StaticCitizenSideBar from "../../components/TopBarSideBar/SideBar/StaticCitizenSideBar"; +import FAQsSection from "./FAQs/FAQs"; +import CitizenHome from "./Home"; +import LanguageSelection from "./Home/LanguageSelection"; +import LocationSelection from "./Home/LocationSelection"; +import UserProfile from "./Home/UserProfile"; +import HowItWorks from "./HowItWorks/howItWorks"; +import Login from "./Login"; +import Search from "./SearchApp"; +import StaticDynamicCard from "./StaticDynamicComponent/StaticDynamicCard"; + +const sidebarHiddenFor = [ + `${window?.contextPath}/citizen/register/name`, + `/${window?.contextPath}/citizen/select-language`, + `/${window?.contextPath}/citizen/select-location`, + `/${window?.contextPath}/citizen/login`, + `/${window?.contextPath}/citizen/register/otp`, +]; + +const getTenants = (codes, tenants) => { + return tenants.filter((tenant) => codes.map((item) => item.code).includes(tenant.code)); +}; + +const Home = ({ + stateInfo, + userDetails, + CITIZEN, + cityDetails, + mobileView, + handleUserDropdownSelection, + logoUrl, + DSO, + stateCode="pg", + modules, + appTenants, + sourceUrl, + pathname, + initData, +}) => { + + const { isLoading: islinkDataLoading, data: linkData, isFetched: isLinkDataFetched } = Digit.Hooks.useCustomMDMS( + Digit.ULBService.getStateId(), + "ACCESSCONTROL-ACTIONS-TEST", + [ + { + name: "actions-test", + filter: `[?(@.url == '${window.contextPath}-card')]`, + }, + ], + { + select: (data) => { + const formattedData = data?.["ACCESSCONTROL-ACTIONS-TEST"]?.["actions-test"] + ?.filter((el) => el.enabled === true) + .reduce((a, b) => { + a[b.parentModule] = a[b.parentModule]?.length > 0 ? [b, ...a[b.parentModule]] : [b]; + return a; + }, {}); + return formattedData; + }, + } + ); + + const classname = Digit.Hooks.useRouteSubscription(pathname); + const { t } = useTranslation(); + const { path } = useRouteMatch(); + const history = useHistory(); + const handleClickOnWhatsApp = (obj) => { + window.open(obj); + }; + + const hideSidebar = sidebarHiddenFor.some((e) => window.location.href.includes(e)); + const appRoutes = modules.map(({ code, tenants }, index) => { + const Module = Digit.ComponentRegistryService.getComponent(`${code}Module`); + return Module ? ( + + + + ) : null; + }); + + const ModuleLevelLinkHomePages = modules.map(({ code, bannerImage }, index) => { + let Links = Digit.ComponentRegistryService.getComponent(`${code}Links`) || (() => ); + let mdmsDataObj = isLinkDataFetched ? processLinkData(linkData, code, t) : undefined; + + if (mdmsDataObj?.header === "ACTION_TEST_WS") { + mdmsDataObj?.links?.sort((a, b) => { + return b.orderNumber - a.orderNumber; + }); + } + return ( + + +
+ noimagefound + +

{t("MODULE_" + code.toUpperCase())}

+
+ {mdmsDataObj && ( + } + Info={ + code === "OBPS" + ? () => ( + + ) + : null + } + isInfo={code === "OBPS" ? true : false} + /> + )} + {/* */} +
+ +
+
+ + + + + + +
+ ); + }); + + return ( +
+ + +
+ {hideSidebar ? null : ( +
+ +
+ )} + + + + + + + + + + + + + + + { + history.push(`/${window?.contextPath}/${Digit?.UserService?.getType?.()}`); + }} + /> + + + + + + + + + + + + + + + + + + + + + + {appRoutes} + {ModuleLevelLinkHomePages} + + +
+
+ Powered by DIGIT { + window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus(); + }} + /> +
+
+ ); +}; + +export default Home; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/changePassword.js b/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/changePassword.js new file mode 100644 index 00000000000..2577c02fe25 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/changePassword.js @@ -0,0 +1,168 @@ +import { BackButton, CardSubHeader, CardText, FormComposer, Toast } from "@digit-ui/digit-ui-react-components"; +import PropTypes from "prop-types"; +import React, { useEffect, useState } from "react"; +import { useHistory } from "react-router-dom"; +import Background from "../../../components/Background"; +import Header from "../../../components/Header"; +import SelectOtp from "../../citizen/Login/SelectOtp"; + + +const ChangePasswordComponent = ({ config: propsConfig, t }) => { + const [user, setUser] = useState(null); + const { mobile_number: mobileNumber, tenantId } = Digit.Hooks.useQueryParams(); + const history = useHistory(); + const [otp, setOtp] = useState(""); + const [isOtpValid, setIsOtpValid] = useState(true); + const [showToast, setShowToast] = useState(null); + const getUserType = () => Digit.UserService.getType(); + useEffect(() => { + if (!user) { + Digit.UserService.setType("employee"); + return; + } + Digit.UserService.setUser(user); + const redirectPath = location.state?.from || `/${window?.contextPath}/employee`; + history.replace(redirectPath); + }, [user]); + + const closeToast = () => { + setShowToast(null); + }; + + const onResendOTP = async () => { + const requestData = { + otp: { + mobileNumber, + userType: getUserType().toUpperCase(), + type: "passwordreset", + tenantId, + }, + }; + + try { + await Digit.UserService.sendOtp(requestData, tenantId); + setShowToast(t("ES_OTP_RESEND")); + } catch (err) { + setShowToast(err?.response?.data?.error_description || t("ES_INVALID_LOGIN_CREDENTIALS")); + } + setTimeout(closeToast, 5000); + }; + + const onChangePassword = async (data) => { + try { + if (data.newPassword !== data.confirmPassword) { + return setShowToast(t("ERR_PASSWORD_DO_NOT_MATCH")); + } + const requestData = { + ...data, + otpReference: otp, + tenantId, + type: getUserType().toUpperCase(), + }; + + const response = await Digit.UserService.changePassword(requestData, tenantId); + navigateToLogin(); + } catch (err) { + setShowToast(err?.response?.data?.error?.fields?.[0]?.message || t("ES_SOMETHING_WRONG")); + setTimeout(closeToast, 5000); + } + }; + + const navigateToLogin = () => { + history.replace(`/${window?.contextPath}/employee/user/login`); + }; + + const [username, password, confirmPassword] = propsConfig.inputs; + const config = [ + { + body: [ + { + label: t(username.label), + type: username.type, + populators: { + name: username.name, + }, + isMandatory: true, + }, + { + label: t(password.label), + type: password.type, + populators: { + name: password.name, + }, + isMandatory: true, + }, + { + label: t(confirmPassword.label), + type: confirmPassword.type, + populators: { + name: confirmPassword.name, + }, + isMandatory: true, + }, + ], + }, + ]; + + return ( + +
+ +
+ +
+ {propsConfig.texts.header} + + {`${t(`CS_LOGIN_OTP_TEXT`)} `} + + {" "} + {`${t(`+ 91 - `)}`} {mobileNumber} + + + + {/*
+ {t("CORE_OTP_SENT_MESSAGE")} + {mobileNumber} + {t("CORE_EMPLOYEE_OTP_CHECK_MESSAGE")} +
+ {t("CORE_OTP_OTP")} * + +
+
+ {t("CORE_OTP_RESEND")} +
+
*/} + + {showToast && } +
+ Powered by DIGIT { + window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus(); + }} + />{" "} +
+ + ); +}; + +ChangePasswordComponent.propTypes = { + loginParams: PropTypes.any, +}; + +ChangePasswordComponent.defaultProps = { + loginParams: null, +}; + +export default ChangePasswordComponent; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/config.js b/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/config.js new file mode 100644 index 00000000000..0dab907d374 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/config.js @@ -0,0 +1,28 @@ +export const config = [ + { + texts: { + header: "CORE_COMMON_RESET_PASSWORD_LABEL", + submitButtonLabel: "CORE_COMMON_CHANGE_PASSWORD", + }, + inputs: [ + { + label: "CORE_LOGIN_USERNAME", + type: "text", + name: "userName", + error: "ERR_HRMS_INVALID_USERNAME", + }, + { + label: "CORE_LOGIN_NEW_PASSWORD", + type: "password", + name: "newPassword", + error: "CORE_COMMON_REQUIRED_ERRMSG", + }, + { + label: "CORE_LOGIN_CONFIRM_NEW_PASSWORD", + type: "password", + name: "confirmPassword", + error: "CORE_COMMON_REQUIRED_ERRMSG", + }, + ], + }, + ]; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/index.js b/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/index.js new file mode 100644 index 00000000000..fd9cc352ace --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/ChangePassword/index.js @@ -0,0 +1,33 @@ +import React, { useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { Route, Switch, useRouteMatch } from "react-router-dom"; +import ChangePasswordComponent from "./changePassword"; +import { config } from "./config"; + +const EmployeeChangePassword = () => { + const { t } = useTranslation(); + const { path } = useRouteMatch(); + + const params = useMemo(() => + config.map( + (step) => { + const texts = {}; + for (const key in step.texts) { + texts[key] = t(step.texts[key]); + } + return { ...step, texts }; + }, + [config] + ) + ); + + return ( + + + + + + ); +}; + +export default EmployeeChangePassword; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/config.js b/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/config.js new file mode 100644 index 00000000000..c447a631133 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/config.js @@ -0,0 +1,23 @@ +export const loginConfig = [ + { + texts: { + header: "CORE_COMMON_FORGOT_PASSWORD_LABEL", + description: "ES_FORGOT_PASSWORD_DESC", + submitButtonLabel: "CORE_COMMON_CONTINUE", + }, + inputs: [ + { + label: "CORE_COMMON_MOBILE_NUMBER", + type: "text", + name: "mobileNumber", + error: "ERR_HRMS_INVALID_MOBILE_NUMBER", + }, + { + label: "CORE_COMMON_CITY", + type: "custom", + name: "city", + error: "ERR_HRMS_INVALID_CITY", + }, + ], + }, + ]; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/forgotPassword.js b/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/forgotPassword.js new file mode 100644 index 00000000000..54f8b61bd1e --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/forgotPassword.js @@ -0,0 +1,142 @@ +import { BackButton, Dropdown, FormComposer, Loader, Toast } from "@digit-ui/digit-ui-react-components"; +import PropTypes from "prop-types"; +import React, { useEffect, useState } from "react"; +import { useHistory } from "react-router-dom"; +import Background from "../../../components/Background"; +import Header from "../../../components/Header"; + +const ForgotPassword = ({ config: propsConfig, t }) => { + const { data: cities, isLoading } = Digit.Hooks.useTenants(); + const [user, setUser] = useState(null); + const history = useHistory(); + const [showToast, setShowToast] = useState(null); + const getUserType = () => Digit.UserService.getType(); + + useEffect(() => { + if (!user) { + Digit.UserService.setType("employee"); + return; + } + Digit.UserService.setUser(user); + const redirectPath = location.state?.from || `/${window?.contextPath}/employee`; + history.replace(redirectPath); + }, [user]); + + const closeToast = () => { + setShowToast(null); + }; + + const onForgotPassword = async (data) => { + if (!data.city) { + alert("Please Select City!"); + return; + } + const requestData = { + otp: { + mobileNumber: data.mobileNumber, + userType: getUserType().toUpperCase(), + type: "passwordreset", + tenantId: data.city.code, + }, + }; + try { + await Digit.UserService.sendOtp(requestData, data.city.code); + history.push(`/${window?.contextPath}/employee/user/change-password?mobile_number=${data.mobileNumber}&tenantId=${data.city.code}`); + } catch (err) { + setShowToast(err?.response?.data?.error?.fields?.[0]?.message || "Invalid login credentials!"); + setTimeout(closeToast, 5000); + } + }; + + const navigateToLogin = () => { + history.replace(`/${window?.contextPath}/employee/login`); + }; + + const [userId, city] = propsConfig.inputs; + const config = [ + { + body: [ + { + label: t(userId.label), + type: userId.type, + populators: { + name: userId.name, + componentInFront: "+91", + }, + isMandatory: true, + }, + { + label: t(city.label), + type: city.type, + populators: { + name: city.name, + customProps: {}, + component: (props, customProps) => ( + { + props.onChange(d); + }} + {...customProps} + /> + ), + }, + isMandatory: true, + }, + ], + }, + ]; + + if (isLoading) { + return ; + } + + return ( + +
+ +
+ +
+ + {showToast && } +
+ Powered by DIGIT { + window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus(); + }} + />{" "} +
+ + ); +}; + +ForgotPassword.propTypes = { + loginParams: PropTypes.any, +}; + +ForgotPassword.defaultProps = { + loginParams: null, +}; + +export default ForgotPassword; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/index.js b/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/index.js new file mode 100644 index 00000000000..a2099cebf5a --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/ForgotPassword/index.js @@ -0,0 +1,33 @@ +import React, { useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { Route, Switch, useRouteMatch } from "react-router-dom"; +import { loginConfig } from "./config"; +import ForgotPasswordComponent from "./forgotPassword"; + +const EmployeeForgotPassword = () => { + const { t } = useTranslation(); + const { path } = useRouteMatch(); + + const params = useMemo(() => + loginConfig.map( + (step) => { + const texts = {}; + for (const key in step.texts) { + texts[key] = t(step.texts[key]); + } + return { ...step, texts }; + }, + [loginConfig] + ) + ); + + return ( + + + + + + ); +}; + +export default EmployeeForgotPassword; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/LanguageSelection/index.js b/micro-ui/web/packages/ui/src/pages/employee/LanguageSelection/index.js new file mode 100644 index 00000000000..4d65752e2a8 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/LanguageSelection/index.js @@ -0,0 +1,61 @@ +import { Card, CustomButton, SubmitBar } from "@digit-ui/digit-ui-react-components"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useHistory } from "react-router-dom"; +import Background from "../../../components/Background"; + +const LanguageSelection = () => { + const { data: storeData, isLoading } = Digit.Hooks.useStore.getInitData(); + const { t,i18n } = useTranslation(); + const history = useHistory(); + const { languages, stateInfo } = storeData || {}; + const selectedLanguage = Digit.StoreData.getCurrentLanguage(); + const [selected, setselected] = useState(selectedLanguage); + const handleChangeLanguage = (language) => { + setselected(language.value); + Digit.LocalizationService.changeLanguage(language.value, stateInfo.code); + i18n.changeLanguage(language.value); + }; + + const handleSubmit = (event) => { + history.push(`/${window?.contextPath}/employee/user/login`); + }; + + if (isLoading) return null; + + return ( + + +
+ Digit + +

{t(`TENANT_TENANTS_${stateInfo?.code.toUpperCase()}`)}

+
+
+ {languages.map((language, index) => ( +
+ handleChangeLanguage(language)} + > +
+ ))} +
+ +
+
+ Powered by DIGIT { + window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus(); + }} + />{" "} +
+
+ ); +}; + +export default LanguageSelection; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/Login/config.js b/micro-ui/web/packages/ui/src/pages/employee/Login/config.js new file mode 100644 index 00000000000..283bbaa3798 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/Login/config.js @@ -0,0 +1,46 @@ +export const loginConfig = [ + { + texts: { + header: "CORE_COMMON_LOGIN", + submitButtonLabel: "CORE_COMMON_CONTINUE", + secondaryButtonLabel: "CORE_COMMON_FORGOT_PASSWORD", + }, + inputs: [ + { + label: "CORE_LOGIN_USERNAME", + type: "text", + populators: { + name: "username", + }, + isMandatory: true, + }, + { + label: "CORE_LOGIN_PASSWORD", + type: "password", + populators: { + name: "password", + }, + isMandatory: true, + }, + { + isMandatory: true, + type: "dropdown", + key: "city", + label: "CORE_COMMON_CITY", + disable: false, + populators: { + name: "city", + optionsKey: "name", + error: "ERR_HRMS_INVALID_CITY", + mdmsConfig: { + masterName: "tenants", + moduleName: "tenant", + localePrefix: "TENANT_TENANTS", + select: + "(data)=>{ return Array.isArray(data['tenant'].tenants) && Digit.Utils.getUnique(data['tenant'].tenants).map(ele=>({code:ele.code,name:Digit.Utils.locale.getTransformedLocale('TENANT_TENANTS_'+ele.code)}))}", + }, + }, + }, + ], + }, + ]; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/Login/index.js b/micro-ui/web/packages/ui/src/pages/employee/Login/index.js new file mode 100644 index 00000000000..2b64ca6a65a --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/Login/index.js @@ -0,0 +1,52 @@ +import React, { useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Route, Switch, useRouteMatch } from "react-router-dom"; +import { loginConfig as defaultLoginConfig } from "./config"; +import LoginComponent from "./login"; + +const EmployeeLogin = () => { + const { t } = useTranslation(); + const { path } = useRouteMatch(); + const [loginConfig, setloginConfig] = useState(defaultLoginConfig); + + const { data: mdmsData, isLoading } = Digit.Hooks.useCommonMDMS(Digit.ULBService.getStateId(), "commonUiConfig", ["LoginConfig"], { + select: (data) => { + return { + config: data?.commonUiConfig?.LoginConfig + }; + }, + retry: false, + }); + + //let loginConfig = mdmsData?.config ? mdmsData?.config : defaultLoginConfig; + useEffect(() => { + if(isLoading == false && mdmsData?.config) + { + setloginConfig(mdmsData?.config) + } + },[mdmsData, isLoading]) + + + const loginParams = useMemo(() => + loginConfig.map( + (step) => { + const texts = {}; + for (const key in step.texts) { + texts[key] = t(step.texts[key]); + } + return { ...step, texts }; + }, + [loginConfig] + ) + ); + + return ( + + + + + + ); +}; + +export default EmployeeLogin; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/Login/login.js b/micro-ui/web/packages/ui/src/pages/employee/Login/login.js new file mode 100644 index 00000000000..dde8d4af39c --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/Login/login.js @@ -0,0 +1,158 @@ +import { BackButton, Dropdown, FormComposer, FormComposerV2, Loader, Toast } from "@digit-ui/digit-ui-react-components"; +import PropTypes from "prop-types"; +import React, { useEffect, useState } from "react"; +import { useHistory } from "react-router-dom"; +import Background from "../../../components/Background"; +import Header from "../../../components/Header"; + +/* set employee details to enable backward compatiable */ +const setEmployeeDetail = (userObject, token) => { + let locale = JSON.parse(sessionStorage.getItem("Digit.locale"))?.value || "en_IN"; + localStorage.setItem("Employee.tenant-id", userObject?.tenantId); + localStorage.setItem("tenant-id", userObject?.tenantId); + localStorage.setItem("citizen.userRequestObject", JSON.stringify(userObject)); + localStorage.setItem("locale", locale); + localStorage.setItem("Employee.locale", locale); + localStorage.setItem("token", token); + localStorage.setItem("Employee.token", token); + localStorage.setItem("user-info", JSON.stringify(userObject)); + localStorage.setItem("Employee.user-info", JSON.stringify(userObject)); +}; + +const Login = ({ config: propsConfig, t, isDisabled }) => { + const { data: cities, isLoading } = Digit.Hooks.useTenants(); + const { data: storeData, isLoading: isStoreLoading } = Digit.Hooks.useStore.getInitData(); + const { stateInfo } = storeData || {}; + const [user, setUser] = useState(null); + const [showToast, setShowToast] = useState(null); + const [disable, setDisable] = useState(false); + + const history = useHistory(); + // const getUserType = () => "EMPLOYEE" || Digit.UserService.getType(); + + useEffect(() => { + if (!user) { + return; + } + Digit.SessionStorage.set("citizen.userRequestObject", user); + const filteredRoles = user?.info?.roles?.filter((role) => role.tenantId === Digit.SessionStorage.get("Employee.tenantId")); + if (user?.info?.roles?.length > 0) user.info.roles = filteredRoles; + Digit.UserService.setUser(user); + setEmployeeDetail(user?.info, user?.access_token); + let redirectPath = `/${window?.contextPath}/employee`; + + /* logic to redirect back to same screen where we left off */ + if (window?.location?.href?.includes("from=")) { + redirectPath = decodeURIComponent(window?.location?.href?.split("from=")?.[1]) || `/${window?.contextPath}/employee`; + } + + /* RAIN-6489 Logic to navigate to National DSS home incase user has only one role [NATADMIN]*/ + if (user?.info?.roles && user?.info?.roles?.every((e) => e.code === "NATADMIN")) { + redirectPath = `/${window?.contextPath}/employee/dss/landing/NURT_DASHBOARD`; + } + /* RAIN-6489 Logic to navigate to National DSS home incase user has only one role [NATADMIN]*/ + if (user?.info?.roles && user?.info?.roles?.every((e) => e.code === "STADMIN")) { + redirectPath = `/${window?.contextPath}/employee/dss/landing/home`; + } + + history.replace(redirectPath); + }, [user]); + + const onLogin = async (data) => { + // if (!data.city) { + // alert("Please Select City!"); + // return; + // } + setDisable(true); + + const requestData = { + ...data, + userType: "EMPLOYEE", + }; + requestData.tenantId = data?.city?.code || Digit.ULBService.getStateId(); + delete requestData.city; + try { + const { UserRequest: info, ...tokens } = await Digit.UserService.authenticate(requestData); + Digit.SessionStorage.set("Employee.tenantId", info?.tenantId); + setUser({ info, ...tokens }); + } catch (err) { + setShowToast( + err?.response?.data?.error_description || + (err?.message == "ES_ERROR_USER_NOT_PERMITTED" && t("ES_ERROR_USER_NOT_PERMITTED")) || + t("INVALID_LOGIN_CREDENTIALS") + ); + setTimeout(closeToast, 5000); + } + setDisable(false); + }; + + const closeToast = () => { + setShowToast(null); + }; + + const onForgotPassword = () => { + history.push(`/${window?.contextPath}/employee/user/forgot-password`); + }; + const defaultValue = { + code: Digit.ULBService.getStateId(), + name: Digit.Utils.locale.getTransformedLocale(`TENANT_TENANTS_${Digit.ULBService.getStateId()}`), + }; + + let config = [{body : propsConfig?.inputs}]; + + const { mode } = Digit.Hooks.useQueryParams(); + if (mode === "admin" && config?.[0]?.body?.[2]?.disable == false && config?.[0]?.body?.[2]?.populators?.defaultValue == undefined) { + config[0].body[2].disable = true; + config[0].body[2].isMandatory = false; + config[0].body[2].populators.defaultValue = defaultValue; + } + return isLoading || isStoreLoading ? ( + + ) : ( + +
+ +
+ + +
+ + {showToast && } +
+ Powered by DIGIT { + window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus(); + }} + />{" "} +
+ + ); +}; + +Login.propTypes = { + loginParams: PropTypes.any, +}; + +Login.defaultProps = { + loginParams: null, +}; + +export default Login; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/employee/index.js b/micro-ui/web/packages/ui/src/pages/employee/index.js new file mode 100644 index 00000000000..9f8fd13e895 --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/employee/index.js @@ -0,0 +1,138 @@ +import React, { lazy, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { Redirect, Route, Switch, useLocation, useRouteMatch, useHistory } from "react-router-dom"; +import { AppModules } from "../../components/AppModules"; +import ErrorBoundary from "../../components/ErrorBoundaries"; +import TopBarSideBar from "../../components/TopBarSideBar"; +import ChangePassword from "./ChangePassword"; +import ForgotPassword from "./ForgotPassword"; +import LanguageSelection from "./LanguageSelection"; +import EmployeeLogin from "./Login"; +import UserProfile from "../citizen/Home/UserProfile"; +import ErrorComponent from "../../components/ErrorComponent"; +import { PrivateRoute } from "@digit-ui/digit-ui-react-components"; + +const userScreensExempted = ["user/profile", "user/error"]; + +const EmployeeApp = ({ + stateInfo, + userDetails, + CITIZEN, + cityDetails, + mobileView, + handleUserDropdownSelection, + logoUrl, + DSO, + stateCode="pg", + modules, + appTenants, + sourceUrl, + pathname, + initData, +}) => { + + const history = useHistory(); + const { t } = useTranslation(); + const { path } = useRouteMatch(); + const location = useLocation(); + const showLanguageChange = location?.pathname?.includes("language-selection"); + const isUserProfile = userScreensExempted.some((url) => location?.pathname?.includes(url)); + useEffect(() => { + Digit.UserService.setType("employee"); + }, []); + + return ( +
+ + + {isUserProfile && ( + + )} +
+ + + + + + + + + + + + + + + { + history.push(`/${window?.contextPath}/${Digit?.UserService?.getType?.()}`); + }} + /> + + + + + + + + +
+
+ + +
+
+ + + +
+
+ Powered by DIGIT { + window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus(); + }} + /> +
+
+
+ + + +
+
+ ); +}; + +export default EmployeeApp; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/pages/index.js b/micro-ui/web/packages/ui/src/pages/index.js new file mode 100644 index 00000000000..fb14482384a --- /dev/null +++ b/micro-ui/web/packages/ui/src/pages/index.js @@ -0,0 +1,83 @@ +import React, { useEffect } from "react"; +import { Redirect, Route, Switch, useHistory, useLocation } from "react-router-dom"; +import CitizenApp from "./citizen"; +import EmployeeApp from "./employee"; + +export const DigitApp = ({ stateCode="pg", modules, appTenants, logoUrl, initData ,defaultLanding="employee",queryClient}) => { + const history = useHistory(); + const { pathname } = useLocation(); + const innerWidth = window.innerWidth; + const cityDetails = Digit.ULBService.getStateId() + const userDetails = Digit.UserService.getUser(); + const { data: storeData } = Digit.Hooks.useStore.getInitData(); + const { stateInfo } = storeData || {}; + + const DSO = Digit.UserService.hasAccess(["FSM_DSO"]); + let CITIZEN = userDetails?.info?.type === "CITIZEN" || !window.location.pathname.split("/").includes("employee") ? true : false; + + if (window.location.pathname.split("/").includes("employee")) CITIZEN = false; + + + + useEffect(() => { + if (!pathname?.includes("application-details")) { + if (!pathname?.includes("inbox")) { + Digit.SessionStorage.del("fsm/inbox/searchParams"); + } + if (pathname?.includes("search")) { + Digit.SessionStorage.del("fsm/search/searchParams"); + } + } + if (!pathname?.includes("dss")) { + Digit.SessionStorage.del("DSS_FILTERS"); + } + if (pathname?.toString() === `/${window?.contextPath}/employee`) { + Digit.SessionStorage.del("SEARCH_APPLICATION_DETAIL"); + Digit.SessionStorage.del("WS_EDIT_APPLICATION_DETAILS"); + } + if (pathname?.toString() === `/${window?.contextPath}/citizen` || pathname?.toString() === `/${window?.contextPath}/employee`) { + Digit.SessionStorage.del("WS_DISCONNECTION"); + } + }, [pathname]); + + history.listen(() => { + window?.scrollTo({ top: 0, left: 0, behavior: "smooth" }); + }); + + const handleUserDropdownSelection = (option) => { + option.func(); + }; + + const mobileView = innerWidth <= 640; + let sourceUrl = `${window.location.origin}/citizen`; + const commonProps = { + stateInfo, + userDetails, + CITIZEN, + cityDetails, + mobileView, + handleUserDropdownSelection, + logoUrl, + DSO, + stateCode, + modules, + appTenants, + sourceUrl, + pathname, + initData, + }; + + return ( + + + + + + + + + + + + ); +}; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/redux/reducers/index.js b/micro-ui/web/packages/ui/src/redux/reducers/index.js new file mode 100644 index 00000000000..c21937ff4ef --- /dev/null +++ b/micro-ui/web/packages/ui/src/redux/reducers/index.js @@ -0,0 +1,8 @@ +export const commonReducer = (defaultData) => (state = defaultData, action) => { + switch (action.type) { + case "LANGUAGE_SELECT": + return { ...state, selectedLanguage: action.payload }; + default: + return state ? state : {}; + } + }; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/redux/store.js b/micro-ui/web/packages/ui/src/redux/store.js new file mode 100644 index 00000000000..e6a71106955 --- /dev/null +++ b/micro-ui/web/packages/ui/src/redux/store.js @@ -0,0 +1,24 @@ +import { applyMiddleware, combineReducers, compose, createStore } from "redux"; +import thunk from "redux-thunk"; +import { commonReducer } from "./reducers"; + +const getRootReducer = (defaultStore, moduleReducers) => + combineReducers({ + common: commonReducer(defaultStore), + ...moduleReducers, + }); + +const middleware = [thunk]; + +const composeEnhancers = + typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose; + +const enhancer = composeEnhancers( + applyMiddleware(...middleware) + // other store enhancers if any +); + +const getStore = (defaultStore, moduleReducers = {}) => { + return createStore(getRootReducer(defaultStore, moduleReducers), enhancer); +}; +export default getStore; \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/utils/index.js b/micro-ui/web/packages/ui/src/utils/index.js new file mode 100644 index 00000000000..683394a9208 --- /dev/null +++ b/micro-ui/web/packages/ui/src/utils/index.js @@ -0,0 +1,3 @@ +export default { + +} \ No newline at end of file diff --git a/micro-ui/web/packages/ui/src/utils/registerRemotes.js b/micro-ui/web/packages/ui/src/utils/registerRemotes.js new file mode 100644 index 00000000000..0e0bb149162 --- /dev/null +++ b/micro-ui/web/packages/ui/src/utils/registerRemotes.js @@ -0,0 +1,136 @@ +//here export a func that basically registers the app using single spa react pkg +//routing and all we have to handle in remote app itself +//need to share history obj i think(we need to rethink the routing part) + +import { registerApplication, start } from 'single-spa'; + +export default (queryClient) => { + const userType = Digit.UserService.getType(); + + // registerApplication({ + // name: "app1", + // app: () => import("app1/App"), + // activeWhen: `/${window.contextPath ? window.contextPath : "core-digit-ui"}/employee/app`, + // customProps: { + // title: "App1 is running on host", + // queryClient, + // userType + // }, + // }); + + // registerApplication({ + // name: 'PGR', + // app: () => import('pgr/PGRModule'), + // activeWhen: `/${ + // window.contextPath ? window.contextPath : 'ui' + // }/${userType}/pgr`, + // customProps: { + // title: 'PGR is running on host', + // queryClient, + // userType, + // }, + // }); + + // registerApplication({ + // name: 'Workbench', + // app: () => import('workbench/WorkbenchModule'), + // activeWhen: `/${ + // window.contextPath ? window.contextPath : 'ui' + // }/employee/workbench`, + // customProps: { + // title: 'Workbench is running on host', + // queryClient, + // userType, + // }, + // }); + + + + // registerApplication({ + // name: 'Microplan', + // app: () => import('microplan/MICROPLANModule'), + // activeWhen: `/${ + // window.contextPath ? window.contextPath : 'ui' + // }/employee/microplan`, + // customProps: { + // title: 'Microplan is running on host', + // queryClient, + // userType, + // }, + // }); + + // registerApplication({ + // name: 'Campaign', + // app: () => import('campaign/CAMPAIGNModule'), + // activeWhen: `/${ + // window.contextPath ? window.contextPath : 'ui' + // }/employee/campaign`, + // customProps: { + // title: 'Campaign is running on host', + // queryClient, + // userType, + // }, + // }); + + registerApplication({ + name: 'HRMS', + app: () => import('hrms/HRMSModule'), + activeWhen: `/${ + window.contextPath ? window.contextPath : 'ui' + }/employee/hrms`, + customProps: { + title: 'HRMS is running on host', + queryClient, + userType, + }, + }); + + // registerApplication({ + // name: "Common", + // app: () => import("common/CommonModule"), + // activeWhen: `/${window.contextPath ? window.contextPath : "ui"}/${userType}/payment`, //change to userType here + // customProps: { + // title: "Common Module is running on host", + // queryClient, + // userType + // }, + // }); + + // registerApplication({ + // name: "Dss", + // app: () => import("dss/DSSModule"), + // activeWhen: `/${window.contextPath ? window.contextPath : "ui"}/employee/dss`, + // customProps: { + // title: "DSS is running on host", + // queryClient, + // userType + // }, + // }); + + // // registerApplication({ + // // name: "TQM", + // // app: () => import("tqm/TQMModule"), + // // activeWhen: `/${window.contextPath ? window.contextPath : "ui"}/employee/tqm`, + // // customProps: { + // // title: "TQM is running on host", + // // queryClient, + // // userType + // // }, + // // }); + + // registerApplication({ + // name: "Engagement", + // app: () => import("engagement/EngagementModule"), + // activeWhen: [ + // `/${window.contextPath ? window.contextPath : "ui"}/${userType}/engagement`, + // `/${window.contextPath ? window.contextPath : "ui"}/${userType}/engagement` + // ], + // customProps: { + // title: "Engagement is running on host", + // queryClient, + // userType + // }, + // }); + + start(); +}; diff --git a/micro-ui/web/packages/ui/webpack.common.js b/micro-ui/web/packages/ui/webpack.common.js new file mode 100644 index 00000000000..d5e86af2510 --- /dev/null +++ b/micro-ui/web/packages/ui/webpack.common.js @@ -0,0 +1,30 @@ +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); + +module.exports = { + entry: "./src/index", + resolve: { + extensions: [ ".js", ".jsx"], + modules: [path.resolve(__dirname, "src"), "node_modules"], + alias: { + 'react-i18next': require.resolve('react-i18next'), + }, + }, + module: { + rules: [ + { + test: /\.js?$/, + loader: "babel-loader", + exclude: /node_modules/, + options: { + presets: ["@babel/preset-react"], + }, + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + template: "./public/index.html", + }), + ], +}; diff --git a/micro-ui/web/packages/ui/webpack.dev.js b/micro-ui/web/packages/ui/webpack.dev.js new file mode 100644 index 00000000000..5bb2801d796 --- /dev/null +++ b/micro-ui/web/packages/ui/webpack.dev.js @@ -0,0 +1,66 @@ +const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); +const { merge } = require("webpack-merge"); +const commonConfig = require("./webpack.common"); +const packageJson = require("./package.json"); +require('dotenv').config({ path: '../../.env' }); + +module.exports = () => { + const devConfig = { + mode: "development", + output: { + publicPath: `https://localhost:8000/`, + filename: "[name].[contenthash].js", + }, + devServer: { + port: 8000, + proxy: [ + { + context: () => true, + target: 'https://unified-dev.digit.org', + secure: true, + changeOrigin:true, + bypass: function (req, res, proxyOptions) { + if (req.headers.accept.indexOf('html') !== -1) { + console.log('Skipping proxy for browser request.'); + return '/index.html'; + } + }, + headers: { + "Connection": "keep-alive" + }, + }, + ], + historyApiFallback: { + index: "/", + }, + server:"https", //Enable HTTPS + }, + plugins: [ + new ModuleFederationPlugin({ + name: "ui", + remotes: { + hrms: "hrms@https://localhost:8085/remoteEntry.js", + // workbench: "workbench@https://localhost:8086/remoteEntry.js", + // // common:"common@https://localhost:8090/remoteEntry.js", + // pgr:"pgr@https://localhost:8087/remoteEntry.js", + // // dss: "dss@https://localhost:8088/remoteEntry.js", + // dss: "dss@https://localhost:8088/remoteEntry.js", + // engagement: "engagement@https://localhost:8091/remoteEntry.js", + // microplan:"microplan@https://localhost:8099/remoteEntry.js", + // tqm: "tqm@https://localhost:8089/remoteEntry.js", + // app1: "app1@https://localhost:8001/remoteEntry.js", + // campaign:"campaign@https://localhost:8005/remoteEntry.js" + + }, + shared: { + ...packageJson.dependencies, + react: { singleton: true }, // React will be shared as a singleton + 'react-dom': { singleton: true }, // ReactDOM will be shared as a singleton + 'react-query': { singleton: true }, + }, + }), + ], + }; + + return merge(commonConfig, devConfig); +}; diff --git a/micro-ui/web/packages/ui/webpack.prod.js b/micro-ui/web/packages/ui/webpack.prod.js new file mode 100644 index 00000000000..7fc507a02fc --- /dev/null +++ b/micro-ui/web/packages/ui/webpack.prod.js @@ -0,0 +1,39 @@ +const { merge } = require("webpack-merge"); +const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); +const commonConfig = require("./webpack.common"); +const packageJson = require("./package.json"); + +const domain = process.env.PRODUCTION_DOMAIN || "https://unified-dev.digit.org"; + +module.exports = () => { + const prodConfig = { + mode: "production", + output: { + publicPath: "/ui/" + }, + plugins: [ + new ModuleFederationPlugin({ + name: "ui", + remotes: { + hrms: `hrms@${domain}/hrms-ui/remoteEntry.js`, + // common: `common@${domain}/common-ui/remoteEntry.js`, + // pgr: `pgr@${domain}/pgr-ui/remoteEntry.js`, + // workbench : `workbench@${domain}/workbench-mfe/remoteEntry.js`, + // dss : `dss@${domain}/dss-ui/remoteEntry.js`, + // engagement : `engagement@${domain}/engagement-ui/remoteEntry.js`, + // microplan : `microplan@${domain}/microplan-mfe/remoteEntry.js`, + // tqm : `tqm@${domain}/tqm-ui/remoteEntry.js`, + // campaign : `campaign@${domain}/campaign-mfe/remoteEntry.js` + }, + shared: { + ...packageJson.dependencies, + react: { singleton: true }, // React will be shared as a singleton + 'react-dom': { singleton: true }, // ReactDOM will be shared as a singleton + 'react-query': { singleton: true }, + }, + }), + ], + }; + + return merge(commonConfig, prodConfig); +};