mirror of
				https://github.com/Mastermindzh/react-starter-kit.git
				synced 2025-11-03 18:28:47 +01:00 
			
		
		
		
	
							
								
								
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@@ -5,13 +5,16 @@
 | 
			
		||||
    "deepmerge",
 | 
			
		||||
    "flexbugs",
 | 
			
		||||
    "Immer",
 | 
			
		||||
    "Keycloak",
 | 
			
		||||
    "languagedetector",
 | 
			
		||||
    "luxon",
 | 
			
		||||
    "oidc",
 | 
			
		||||
    "pmmmwh",
 | 
			
		||||
    "preinstall",
 | 
			
		||||
    "reduxjs",
 | 
			
		||||
    "SVGR",
 | 
			
		||||
    "tailwindcss",
 | 
			
		||||
    "tendersguru",
 | 
			
		||||
    "testid",
 | 
			
		||||
    "typeahead",
 | 
			
		||||
    "uncompiled"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.vscode/typescriptreact.code-snippets
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.vscode/typescriptreact.code-snippets
									
									
									
									
										vendored
									
									
								
							@@ -68,5 +68,9 @@
 | 
			
		||||
      "",
 | 
			
		||||
      "export default ${1:${TM_FILENAME_BASE}}Slice.reducer;"
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "react-i18next useTranslate hook": {
 | 
			
		||||
    "prefix": ["useTranslation", "translate", "i18-trans"],
 | 
			
		||||
    "body": ["const [translate] = useTranslation();"]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
 | 
			
		||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 | 
			
		||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 | 
			
		||||
 | 
			
		||||
## [0.5.0] - 2022-06-26
 | 
			
		||||
 | 
			
		||||
- Added react-oidc (use demo/demo)
 | 
			
		||||
- Added an example of an authentication protected page (tenders)
 | 
			
		||||
- Added an example with the built in proxy (to combat CORS) (tendersguru)
 | 
			
		||||
 | 
			
		||||
## [0.4.1] - 2022-06-26
 | 
			
		||||
 | 
			
		||||
- Moved examples into example directory
 | 
			
		||||
- Moved routes to separate file
 | 
			
		||||
- Used route constants
 | 
			
		||||
 | 
			
		||||
## [0.4.0] - 2022-07-25
 | 
			
		||||
 | 
			
		||||
- Added the possibility to override partial configs during deployments
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@@ -17,6 +17,7 @@ Includes:
 | 
			
		||||
 | 
			
		||||
- [Getting started](#getting-started)
 | 
			
		||||
- [Project structure](#project-structure)
 | 
			
		||||
- ["Forking" outside of Github](#forking-outside-of-github)
 | 
			
		||||
- [Configuration](#configuration)
 | 
			
		||||
  - [Using the `config.ts` file](#using-the-configts-file)
 | 
			
		||||
  - [adding values](#adding-values)
 | 
			
		||||
@@ -56,6 +57,17 @@ Only the important files are shown
 | 
			
		||||
└── tsconfig.json
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## "Forking" outside of Github
 | 
			
		||||
 | 
			
		||||
To use this base in other git software (not Github) you will have to manually manage the upstream.
 | 
			
		||||
Go into your existing repo and execute the following commands:
 | 
			
		||||
 | 
			
		||||
1. `git remote add upstream <clone-url>`
 | 
			
		||||
2. `git pull upstream master` # or other branchname
 | 
			
		||||
3. `git push`
 | 
			
		||||
 | 
			
		||||
Then, when you need to sync again you can repeat step 2 and 3
 | 
			
		||||
 | 
			
		||||
## Configuration
 | 
			
		||||
 | 
			
		||||
This starter kit comes with runtime configuration out-of-the-box.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										142
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										142
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1,15 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "react-starter-kit",
 | 
			
		||||
  "version": "0.4.0",
 | 
			
		||||
  "version": "0.4.1",
 | 
			
		||||
  "lockfileVersion": 2,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "packages": {
 | 
			
		||||
    "": {
 | 
			
		||||
      "name": "react-starter-kit",
 | 
			
		||||
      "version": "0.4.0",
 | 
			
		||||
      "version": "0.4.1",
 | 
			
		||||
      "hasInstallScript": true,
 | 
			
		||||
      "license": "MIT",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@axa-fr/react-oidc": "^6.0.0-beta10",
 | 
			
		||||
        "@reduxjs/toolkit": "^1.8.3",
 | 
			
		||||
        "deepmerge": "^4.2.2",
 | 
			
		||||
        "i18next-http-backend": "^1.4.1",
 | 
			
		||||
@@ -60,6 +61,7 @@
 | 
			
		||||
        "file-loader": "^6.2.0",
 | 
			
		||||
        "fs-extra": "^10.1.0",
 | 
			
		||||
        "html-webpack-plugin": "^5.5.0",
 | 
			
		||||
        "http-proxy-middleware": "^2.0.6",
 | 
			
		||||
        "husky": "^8.0.1",
 | 
			
		||||
        "i18next": "^21.8.14",
 | 
			
		||||
        "i18next-browser-languagedetector": "^6.1.4",
 | 
			
		||||
@@ -112,6 +114,18 @@
 | 
			
		||||
        "node": ">=6.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@axa-fr/react-oidc": {
 | 
			
		||||
      "version": "6.0.0-beta10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@axa-fr/react-oidc/-/react-oidc-6.0.0-beta10.tgz",
 | 
			
		||||
      "integrity": "sha512-BWHuIn9b+5O35It+V3m/8MWAV6pOjoMy2SihKfeDlsQs2Bp4HMBb4x46zygZ1IGs/u43jtTsnZkrPhwJU6VdqQ==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@openid/appauth": "1.3.1"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "react": "x",
 | 
			
		||||
        "react-dom": "x"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@babel/code-frame": {
 | 
			
		||||
      "version": "7.18.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
 | 
			
		||||
@@ -3309,6 +3323,32 @@
 | 
			
		||||
        "node": ">= 8"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@openid/appauth": {
 | 
			
		||||
      "version": "1.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@openid/appauth/-/appauth-1.3.1.tgz",
 | 
			
		||||
      "integrity": "sha512-e54kpi219wES2ijPzeHe1kMnT8VKH8YeTd1GAn9BzVBmutz3tBgcG1y8a4pziNr4vNjFnuD4W446Ua7ELnNDiA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/base64-js": "^1.3.0",
 | 
			
		||||
        "@types/jquery": "^3.5.5",
 | 
			
		||||
        "base64-js": "^1.5.1",
 | 
			
		||||
        "follow-redirects": "^1.13.3",
 | 
			
		||||
        "form-data": "^4.0.0",
 | 
			
		||||
        "opener": "^1.5.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@openid/appauth/node_modules/form-data": {
 | 
			
		||||
      "version": "4.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "asynckit": "^0.4.0",
 | 
			
		||||
        "combined-stream": "^1.0.8",
 | 
			
		||||
        "mime-types": "^2.1.12"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 6"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@pmmmwh/react-refresh-webpack-plugin": {
 | 
			
		||||
      "version": "0.5.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz",
 | 
			
		||||
@@ -4019,6 +4059,11 @@
 | 
			
		||||
        "@babel/types": "^7.3.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/base64-js": {
 | 
			
		||||
      "version": "1.3.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/base64-js/-/base64-js-1.3.0.tgz",
 | 
			
		||||
      "integrity": "sha512-ZmI0sZGAUNXUfMWboWwi4LcfpoVUYldyN6Oe0oJ5cCsHDU/LlRq8nQKPXhYLOx36QYSW9bNIb1vvRrD6K7Llgw=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/body-parser": {
 | 
			
		||||
      "version": "1.19.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
 | 
			
		||||
@@ -4203,6 +4248,14 @@
 | 
			
		||||
      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/jquery": {
 | 
			
		||||
      "version": "3.5.14",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
 | 
			
		||||
      "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/sizzle": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/json-schema": {
 | 
			
		||||
      "version": "7.0.11",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
 | 
			
		||||
@@ -4338,8 +4391,7 @@
 | 
			
		||||
    "node_modules/@types/sizzle": {
 | 
			
		||||
      "version": "2.3.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
 | 
			
		||||
      "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/sockjs": {
 | 
			
		||||
      "version": "0.3.33",
 | 
			
		||||
@@ -5252,8 +5304,7 @@
 | 
			
		||||
    "node_modules/asynckit": {
 | 
			
		||||
      "version": "0.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/at-least-node": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
@@ -5666,7 +5717,6 @@
 | 
			
		||||
      "version": "1.5.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
 | 
			
		||||
      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "funding": [
 | 
			
		||||
        {
 | 
			
		||||
          "type": "github",
 | 
			
		||||
@@ -6354,7 +6404,6 @@
 | 
			
		||||
      "version": "1.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
 | 
			
		||||
      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "delayed-stream": "~1.0.0"
 | 
			
		||||
      },
 | 
			
		||||
@@ -7540,7 +7589,6 @@
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=0.4.0"
 | 
			
		||||
      }
 | 
			
		||||
@@ -9636,7 +9684,6 @@
 | 
			
		||||
      "version": "1.15.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
 | 
			
		||||
      "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "funding": [
 | 
			
		||||
        {
 | 
			
		||||
          "type": "individual",
 | 
			
		||||
@@ -15653,6 +15700,14 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/sindresorhus"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/opener": {
 | 
			
		||||
      "version": "1.5.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
 | 
			
		||||
      "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
 | 
			
		||||
      "bin": {
 | 
			
		||||
        "opener": "bin/opener-bin.js"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/optionator": {
 | 
			
		||||
      "version": "0.9.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
 | 
			
		||||
@@ -21337,6 +21392,14 @@
 | 
			
		||||
        "@jridgewell/trace-mapping": "^0.3.9"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@axa-fr/react-oidc": {
 | 
			
		||||
      "version": "6.0.0-beta10",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@axa-fr/react-oidc/-/react-oidc-6.0.0-beta10.tgz",
 | 
			
		||||
      "integrity": "sha512-BWHuIn9b+5O35It+V3m/8MWAV6pOjoMy2SihKfeDlsQs2Bp4HMBb4x46zygZ1IGs/u43jtTsnZkrPhwJU6VdqQ==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@openid/appauth": "1.3.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@babel/code-frame": {
 | 
			
		||||
      "version": "7.18.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
 | 
			
		||||
@@ -23588,6 +23651,31 @@
 | 
			
		||||
        "fastq": "^1.6.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@openid/appauth": {
 | 
			
		||||
      "version": "1.3.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@openid/appauth/-/appauth-1.3.1.tgz",
 | 
			
		||||
      "integrity": "sha512-e54kpi219wES2ijPzeHe1kMnT8VKH8YeTd1GAn9BzVBmutz3tBgcG1y8a4pziNr4vNjFnuD4W446Ua7ELnNDiA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@types/base64-js": "^1.3.0",
 | 
			
		||||
        "@types/jquery": "^3.5.5",
 | 
			
		||||
        "base64-js": "^1.5.1",
 | 
			
		||||
        "follow-redirects": "^1.13.3",
 | 
			
		||||
        "form-data": "^4.0.0",
 | 
			
		||||
        "opener": "^1.5.2"
 | 
			
		||||
      },
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "form-data": {
 | 
			
		||||
          "version": "4.0.0",
 | 
			
		||||
          "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
 | 
			
		||||
          "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
 | 
			
		||||
          "requires": {
 | 
			
		||||
            "asynckit": "^0.4.0",
 | 
			
		||||
            "combined-stream": "^1.0.8",
 | 
			
		||||
            "mime-types": "^2.1.12"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@pmmmwh/react-refresh-webpack-plugin": {
 | 
			
		||||
      "version": "0.5.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz",
 | 
			
		||||
@@ -24067,6 +24155,11 @@
 | 
			
		||||
        "@babel/types": "^7.3.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@types/base64-js": {
 | 
			
		||||
      "version": "1.3.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/base64-js/-/base64-js-1.3.0.tgz",
 | 
			
		||||
      "integrity": "sha512-ZmI0sZGAUNXUfMWboWwi4LcfpoVUYldyN6Oe0oJ5cCsHDU/LlRq8nQKPXhYLOx36QYSW9bNIb1vvRrD6K7Llgw=="
 | 
			
		||||
    },
 | 
			
		||||
    "@types/body-parser": {
 | 
			
		||||
      "version": "1.19.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
 | 
			
		||||
@@ -24244,6 +24337,14 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@types/jquery": {
 | 
			
		||||
      "version": "3.5.14",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
 | 
			
		||||
      "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@types/sizzle": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@types/json-schema": {
 | 
			
		||||
      "version": "7.0.11",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
 | 
			
		||||
@@ -24379,8 +24480,7 @@
 | 
			
		||||
    "@types/sizzle": {
 | 
			
		||||
      "version": "2.3.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
 | 
			
		||||
      "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "@types/sockjs": {
 | 
			
		||||
      "version": "0.3.33",
 | 
			
		||||
@@ -25058,8 +25158,7 @@
 | 
			
		||||
    "asynckit": {
 | 
			
		||||
      "version": "0.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
 | 
			
		||||
    },
 | 
			
		||||
    "at-least-node": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
@@ -25369,8 +25468,7 @@
 | 
			
		||||
    "base64-js": {
 | 
			
		||||
      "version": "1.5.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
 | 
			
		||||
      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
 | 
			
		||||
    },
 | 
			
		||||
    "batch": {
 | 
			
		||||
      "version": "0.6.1",
 | 
			
		||||
@@ -25874,7 +25972,6 @@
 | 
			
		||||
      "version": "1.0.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
 | 
			
		||||
      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "delayed-stream": "~1.0.0"
 | 
			
		||||
      }
 | 
			
		||||
@@ -26742,8 +26839,7 @@
 | 
			
		||||
    "delayed-stream": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "depd": {
 | 
			
		||||
      "version": "2.0.0",
 | 
			
		||||
@@ -28331,8 +28427,7 @@
 | 
			
		||||
    "follow-redirects": {
 | 
			
		||||
      "version": "1.15.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
 | 
			
		||||
      "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
      "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
 | 
			
		||||
    },
 | 
			
		||||
    "forever-agent": {
 | 
			
		||||
      "version": "0.6.1",
 | 
			
		||||
@@ -32760,6 +32855,11 @@
 | 
			
		||||
        "is-wsl": "^2.2.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "opener": {
 | 
			
		||||
      "version": "1.5.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
 | 
			
		||||
      "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="
 | 
			
		||||
    },
 | 
			
		||||
    "optionator": {
 | 
			
		||||
      "version": "0.9.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "react-starter-kit",
 | 
			
		||||
  "version": "0.4.0",
 | 
			
		||||
  "version": "0.5.0",
 | 
			
		||||
  "description": "A modern, create-react-app-based, starter kit for React projects",
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "react",
 | 
			
		||||
@@ -45,6 +45,7 @@
 | 
			
		||||
    "*.{ts,js,jsx,tsx,css,scss,json,md}": "prettier --write"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@axa-fr/react-oidc": "^6.0.0-beta10",
 | 
			
		||||
    "@reduxjs/toolkit": "^1.8.3",
 | 
			
		||||
    "deepmerge": "^4.2.2",
 | 
			
		||||
    "i18next-http-backend": "^1.4.1",
 | 
			
		||||
@@ -95,6 +96,7 @@
 | 
			
		||||
    "file-loader": "^6.2.0",
 | 
			
		||||
    "fs-extra": "^10.1.0",
 | 
			
		||||
    "html-webpack-plugin": "^5.5.0",
 | 
			
		||||
    "http-proxy-middleware": "^2.0.6",
 | 
			
		||||
    "husky": "^8.0.1",
 | 
			
		||||
    "i18next": "^21.8.14",
 | 
			
		||||
    "i18next-browser-languagedetector": "^6.1.4",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,24 @@
 | 
			
		||||
const defaultConfig = {
 | 
			
		||||
  version: "0.1.0",
 | 
			
		||||
  name: "React-starter-kit",
 | 
			
		||||
  // oidc: {
 | 
			
		||||
  //   url: "private_url",
 | 
			
		||||
  //   realm: "inforit",
 | 
			
		||||
  //   clientId: "react-base",
 | 
			
		||||
  //   scope: "openid profile email",
 | 
			
		||||
  //   redirectUri: "http://localhost:3000/authentication/callback",
 | 
			
		||||
  //   silentRedirectUri: "http://localhost:3000/authentication/silent-callback",
 | 
			
		||||
  //   serviceWorkerOnly: false,
 | 
			
		||||
  // },
 | 
			
		||||
  oidc: {
 | 
			
		||||
    url: "https://sso.mastermindzh.tech/realms/public-tests",
 | 
			
		||||
    realm: "public-tests",
 | 
			
		||||
    clientId: "react-starter-kit",
 | 
			
		||||
    scope: "openid profile email",
 | 
			
		||||
    redirectUri: "http://localhost:3000/authentication/callback",
 | 
			
		||||
    silentRedirectUri: "http://localhost:3000/authentication/silent-callback",
 | 
			
		||||
    serviceWorkerOnly: false,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// ignore this :)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,8 @@
 | 
			
		||||
  "nav": {
 | 
			
		||||
    "home": "home",
 | 
			
		||||
    "about": "about",
 | 
			
		||||
    "counter": "counter"
 | 
			
		||||
    "counter": "counter",
 | 
			
		||||
    "tenders": "tenders (with auth)"
 | 
			
		||||
  },
 | 
			
		||||
  "about": {
 | 
			
		||||
    "title": "About"
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,8 @@
 | 
			
		||||
  "nav": {
 | 
			
		||||
    "home": "home",
 | 
			
		||||
    "about": "over ons",
 | 
			
		||||
    "counter": "teller"
 | 
			
		||||
    "counter": "teller",
 | 
			
		||||
    "tenders": "aanbestedingen (met auth)"
 | 
			
		||||
  },
 | 
			
		||||
  "about": {
 | 
			
		||||
    "title": "Over ons"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/App.tsx
									
									
									
									
									
								
							@@ -1,26 +1,13 @@
 | 
			
		||||
import { BrowserRouter, Route, Routes } from "react-router-dom";
 | 
			
		||||
import { AboutContainer } from "./features/about/About";
 | 
			
		||||
import { CounterContainer } from "./features/counter/Counter";
 | 
			
		||||
import { HomeContainer } from "./features/home/Home";
 | 
			
		||||
import { BrowserRouter } from "react-router-dom";
 | 
			
		||||
import { Navbar } from "./infrastructure/navbar/Navbar";
 | 
			
		||||
import { AppRoutes } from "./Routes";
 | 
			
		||||
 | 
			
		||||
function App() {
 | 
			
		||||
  return (
 | 
			
		||||
    <BrowserRouter>
 | 
			
		||||
      <div className="App">
 | 
			
		||||
        <Navbar />
 | 
			
		||||
        <Routes>
 | 
			
		||||
          <Route path="/" element={<HomeContainer />} />
 | 
			
		||||
          <Route path="/about" element={<AboutContainer />} />
 | 
			
		||||
          <Route path="/counter" element={<CounterContainer />} />
 | 
			
		||||
          {/* <Route index element={<Home />} /> */}
 | 
			
		||||
          {/* <Route path="teams" element={<Teams />}>
 | 
			
		||||
              <Route path=":teamId" element={<Team />} />
 | 
			
		||||
              <Route path="new" element={<NewTeamForm />} />
 | 
			
		||||
              <Route index element={<LeagueStandings />} />
 | 
			
		||||
            </Route> */}
 | 
			
		||||
          {/* </Route> */}
 | 
			
		||||
        </Routes>
 | 
			
		||||
        <AppRoutes />
 | 
			
		||||
      </div>
 | 
			
		||||
    </BrowserRouter>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								src/Routes.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Routes.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
import { OidcSecure } from "@axa-fr/react-oidc";
 | 
			
		||||
import { FunctionComponent } from "react";
 | 
			
		||||
import { Route, Routes } from "react-router-dom";
 | 
			
		||||
import { AboutContainer } from "./features/about/About";
 | 
			
		||||
import { CounterContainer } from "./features/examples/counter/Counter";
 | 
			
		||||
import { Tenders } from "./features/examples/tenders/Tenders";
 | 
			
		||||
import { HomeContainer } from "./features/home/Home";
 | 
			
		||||
type Props = {};
 | 
			
		||||
 | 
			
		||||
export const ROUTE_KEYS = {
 | 
			
		||||
  home: "",
 | 
			
		||||
  about: "about",
 | 
			
		||||
  counter: "counter",
 | 
			
		||||
  tenders: "tenders",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const AppRoutes: FunctionComponent<Props> = () => {
 | 
			
		||||
  const { home, about, counter, tenders } = ROUTE_KEYS;
 | 
			
		||||
  return (
 | 
			
		||||
    <Routes>
 | 
			
		||||
      <Route path={home} element={<HomeContainer />} />
 | 
			
		||||
      <Route path={about} element={<AboutContainer />} />
 | 
			
		||||
      <Route path={counter} element={<CounterContainer />} />
 | 
			
		||||
      {/* a route with authentication */}
 | 
			
		||||
      <Route
 | 
			
		||||
        path={tenders}
 | 
			
		||||
        element={
 | 
			
		||||
          <OidcSecure>
 | 
			
		||||
            <Tenders />
 | 
			
		||||
          </OidcSecure>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {/* <Route index element={<Home />} /> */}
 | 
			
		||||
      {/* <Route path="teams" element={<Teams />}>
 | 
			
		||||
      <Route path=":teamId" element={<Team />} />
 | 
			
		||||
      <Route path="new" element={<NewTeamForm />} />
 | 
			
		||||
      <Route index element={<LeagueStandings />} />
 | 
			
		||||
    </Route> */}
 | 
			
		||||
      {/* </Route> */}
 | 
			
		||||
    </Routes>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
 | 
			
		||||
import counterReducer from "../features/counter/state/counterSlice";
 | 
			
		||||
import counterReducer from "../features/examples/counter/state/counterSlice";
 | 
			
		||||
 | 
			
		||||
export const store = configureStore({
 | 
			
		||||
  reducer: {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,14 @@ import { FunctionComponent, ReactNode } from "react";
 | 
			
		||||
import { I18nextProvider, useTranslation } from "react-i18next";
 | 
			
		||||
import i18n from "./i18n";
 | 
			
		||||
 | 
			
		||||
type Props = { children?: ReactNode; keysOnly?: boolean };
 | 
			
		||||
type Props = {
 | 
			
		||||
  children?: ReactNode;
 | 
			
		||||
  /**
 | 
			
		||||
   * Whether to show the translation keys instead of translated text
 | 
			
		||||
   * Can be useful to test languages that don't have full translations
 | 
			
		||||
   */
 | 
			
		||||
  keysOnly?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ProvidedComponent: FunctionComponent<Props> = ({ children, keysOnly }) => {
 | 
			
		||||
  const [_translate, i18nSettings] = useTranslation();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
// A mock function to mimic making an async request for data
 | 
			
		||||
export function fetchCount(amount = 1) {
 | 
			
		||||
  return new Promise<{ data: number }>((resolve) => setTimeout(() => resolve({ data: amount }), 500));
 | 
			
		||||
}
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
  padding-left: 16px;
 | 
			
		||||
  padding-right: 16px;
 | 
			
		||||
  margin-top: 2px;
 | 
			
		||||
  font-family: 'Courier New', Courier, monospace;
 | 
			
		||||
  font-family: "Courier New", Courier, monospace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.button {
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.asyncButton:after {
 | 
			
		||||
  content: '';
 | 
			
		||||
  content: "";
 | 
			
		||||
  background-color: rgba(112, 76, 182, 0.15);
 | 
			
		||||
  display: block;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import { useState } from "react";
 | 
			
		||||
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
 | 
			
		||||
import { useAppDispatch, useAppSelector } from "../../../app/hooks";
 | 
			
		||||
import styles from "./Counter.module.css";
 | 
			
		||||
import { incrementAsync } from "./state/actions/incrementAsync";
 | 
			
		||||
import {
 | 
			
		||||
							
								
								
									
										6
									
								
								src/features/examples/counter/services/counterAPI.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/features/examples/counter/services/counterAPI.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
// A mock function to mimic making an async request for data
 | 
			
		||||
export function fetchCount(amount = 1) {
 | 
			
		||||
  return new Promise<{ data: number }>((resolve) =>
 | 
			
		||||
    setTimeout(() => resolve({ data: amount }), 500),
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,4 @@
 | 
			
		||||
import counterReducer, {
 | 
			
		||||
  increment,
 | 
			
		||||
  decrement,
 | 
			
		||||
  incrementByAmount,
 | 
			
		||||
} from "./counterSlice";
 | 
			
		||||
import counterReducer, { increment, decrement, incrementByAmount } from "./counterSlice";
 | 
			
		||||
import { CounterState } from "../models/CounterState";
 | 
			
		||||
 | 
			
		||||
describe("counter reducer", () => {
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 | 
			
		||||
import { AppThunk, RootState } from "../../../app/store";
 | 
			
		||||
import { AppThunk, RootState } from "../../../../app/store";
 | 
			
		||||
import { CounterState } from "../models/CounterState";
 | 
			
		||||
import { incrementAsync } from "./actions/incrementAsync";
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								src/features/examples/tenders/Tenders.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/features/examples/tenders/Tenders.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
import { useOidcAccessToken } from "@axa-fr/react-oidc";
 | 
			
		||||
import { FunctionComponent, useEffect, useState } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
 | 
			
		||||
type Props = {};
 | 
			
		||||
 | 
			
		||||
export const Tenders: FunctionComponent<Props> = () => {
 | 
			
		||||
  const [tenders, setTenders] = useState(null);
 | 
			
		||||
  const [translate] = useTranslation();
 | 
			
		||||
  const { accessToken } = useOidcAccessToken();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // this uses the CORS proxy in our webpack setup
 | 
			
		||||
    // tendersguru is mapped to "https://tenders.guru" in setupProxy.js
 | 
			
		||||
    // it will strip the tendersguru part, so the full url will be: https://tenders.guru/api/ro/tenders
 | 
			
		||||
    fetch("tendersguru/api/ro/tenders", {
 | 
			
		||||
      // fetch's way of adding headers. Not required to access the api... but :shrug:
 | 
			
		||||
      headers: new Headers({
 | 
			
		||||
        Authorization: `Bearer ${accessToken}`,
 | 
			
		||||
      }),
 | 
			
		||||
    })
 | 
			
		||||
      .then((response) => response.json())
 | 
			
		||||
      .then((data) => {
 | 
			
		||||
        setTenders(data);
 | 
			
		||||
      });
 | 
			
		||||
  }, [accessToken]);
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <h1>{translate("nav.tenders")}</h1>
 | 
			
		||||
      {tenders ? <pre>{JSON.stringify(tenders, null, 2)}</pre> : <h2>loading...</h2>}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -8,19 +8,39 @@ export const HomeContainer: FunctionComponent<Props> = () => {
 | 
			
		||||
      <p>This is the react base app :)</p>
 | 
			
		||||
      <span>
 | 
			
		||||
        <span>Learn </span>
 | 
			
		||||
        <a className="App-link" href="https://reactjs.org/" target="_blank" rel="noopener noreferrer">
 | 
			
		||||
        <a
 | 
			
		||||
          className="App-link"
 | 
			
		||||
          href="https://reactjs.org/"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
          rel="noopener noreferrer"
 | 
			
		||||
        >
 | 
			
		||||
          React
 | 
			
		||||
        </a>
 | 
			
		||||
        <span>, </span>
 | 
			
		||||
        <a className="App-link" href="https://redux.js.org/" target="_blank" rel="noopener noreferrer">
 | 
			
		||||
        <a
 | 
			
		||||
          className="App-link"
 | 
			
		||||
          href="https://redux.js.org/"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
          rel="noopener noreferrer"
 | 
			
		||||
        >
 | 
			
		||||
          Redux
 | 
			
		||||
        </a>
 | 
			
		||||
        <span>, </span>
 | 
			
		||||
        <a className="App-link" href="https://redux-toolkit.js.org/" target="_blank" rel="noopener noreferrer">
 | 
			
		||||
        <a
 | 
			
		||||
          className="App-link"
 | 
			
		||||
          href="https://redux-toolkit.js.org/"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
          rel="noopener noreferrer"
 | 
			
		||||
        >
 | 
			
		||||
          Redux Toolkit
 | 
			
		||||
        </a>
 | 
			
		||||
        ,<span> and </span>
 | 
			
		||||
        <a className="App-link" href="https://react-redux.js.org/" target="_blank" rel="noopener noreferrer">
 | 
			
		||||
        <a
 | 
			
		||||
          className="App-link"
 | 
			
		||||
          href="https://react-redux.js.org/"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
          rel="noopener noreferrer"
 | 
			
		||||
        >
 | 
			
		||||
          React Redux
 | 
			
		||||
        </a>
 | 
			
		||||
      </span>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import App from "./App";
 | 
			
		||||
import { store } from "./app/store";
 | 
			
		||||
import "./index.css";
 | 
			
		||||
import "./infrastructure/i18n/init";
 | 
			
		||||
import { OidcProvider } from "./infrastructure/sso/OidcProvider";
 | 
			
		||||
import { Loader } from "./infrastructure/wrappers/WithPageSuspense";
 | 
			
		||||
import reportWebVitals from "./reportWebVitals";
 | 
			
		||||
 | 
			
		||||
@@ -14,9 +15,11 @@ const root = createRoot(container);
 | 
			
		||||
root.render(
 | 
			
		||||
  <React.StrictMode>
 | 
			
		||||
    <Provider store={store}>
 | 
			
		||||
      <Loader>
 | 
			
		||||
        <App />
 | 
			
		||||
      </Loader>
 | 
			
		||||
      <OidcProvider>
 | 
			
		||||
        <Loader>
 | 
			
		||||
          <App />
 | 
			
		||||
        </Loader>
 | 
			
		||||
      </OidcProvider>
 | 
			
		||||
    </Provider>
 | 
			
		||||
  </React.StrictMode>,
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,11 @@
 | 
			
		||||
import { OIDCConfig } from "../sso/models/OIDCConfig";
 | 
			
		||||
 | 
			
		||||
export interface RunTimeConfig {
 | 
			
		||||
  version: number;
 | 
			
		||||
  name: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Settings for the OIDC connection
 | 
			
		||||
   */
 | 
			
		||||
  oidc: OIDCConfig;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import deepmerge from "deepmerge";
 | 
			
		||||
import { RunTimeConfig } from "./RunTimeConfig";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * gets and merges both the regular config and the override config from the window
 | 
			
		||||
@@ -12,4 +13,4 @@ export const mergeConfigs = () => {
 | 
			
		||||
 | 
			
		||||
mergeConfigs();
 | 
			
		||||
 | 
			
		||||
export const Config = window.mergedConfig;
 | 
			
		||||
export const Config: RunTimeConfig = window.mergedConfig;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,25 @@
 | 
			
		||||
import { render, screen } from "@testing-library/react";
 | 
			
		||||
import { render, screen, waitFor } from "@testing-library/react";
 | 
			
		||||
import { WithTestTranslations } from "../../app/tests/mocks/i18n/WithTestTranslations";
 | 
			
		||||
import { OidcProvider } from "../sso/OidcProvider";
 | 
			
		||||
import { WithRouter } from "../wrappers/WithRouter";
 | 
			
		||||
import { Navbar } from "./Navbar";
 | 
			
		||||
 | 
			
		||||
describe("Navbar container", () => {
 | 
			
		||||
  it.only("renders a navigation section identified by the nav test-id", () => {
 | 
			
		||||
  it.only("renders a navigation section identified by the nav test-id", async () => {
 | 
			
		||||
    render(
 | 
			
		||||
      <WithTestTranslations>
 | 
			
		||||
        <WithRouter>
 | 
			
		||||
          <Navbar />
 | 
			
		||||
        </WithRouter>
 | 
			
		||||
        {/* for simple tests where we don't need auth we don't actually have to mock responses */}
 | 
			
		||||
        <OidcProvider>
 | 
			
		||||
          <WithRouter>
 | 
			
		||||
            <Navbar />
 | 
			
		||||
          </WithRouter>
 | 
			
		||||
        </OidcProvider>
 | 
			
		||||
      </WithTestTranslations>,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    expect(screen.getAllByTestId("nav")?.length).toBeGreaterThan(0);
 | 
			
		||||
    // because of the extra loaders we wait for a result just to be sure
 | 
			
		||||
    // see: https://testing-library.com/docs/guide-disappearance/
 | 
			
		||||
    await waitFor(() => {
 | 
			
		||||
      expect(screen.getAllByTestId("nav")?.length).toBeGreaterThan(0);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
import { useOidc, useOidcAccessToken } from "@axa-fr/react-oidc";
 | 
			
		||||
import { DateTime } from "luxon";
 | 
			
		||||
import { FunctionComponent } from "react";
 | 
			
		||||
import { Trans, useTranslation } from "react-i18next";
 | 
			
		||||
import { Link } from "react-router-dom";
 | 
			
		||||
import { ROUTE_KEYS } from "../../Routes";
 | 
			
		||||
import { Config } from "../config";
 | 
			
		||||
import "./Navbar.css";
 | 
			
		||||
type Props = {};
 | 
			
		||||
 | 
			
		||||
export const Navbar: FunctionComponent<Props> = () => {
 | 
			
		||||
  const [translate, i18n] = useTranslation();
 | 
			
		||||
  const { login, logout, isAuthenticated } = useOidc();
 | 
			
		||||
  const { accessTokenPayload } = useOidcAccessToken();
 | 
			
		||||
  const { home, about, counter } = ROUTE_KEYS;
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <h1>{translate("navBar.intro")}</h1>
 | 
			
		||||
@@ -15,20 +20,30 @@ export const Navbar: FunctionComponent<Props> = () => {
 | 
			
		||||
        {/* trans can also be used to translate */}
 | 
			
		||||
        {Config.name} <Trans i18nKey="navBar.version">version:</Trans>
 | 
			
		||||
        {JSON.stringify(Config.version)}
 | 
			
		||||
        <button
 | 
			
		||||
          onClick={() => {
 | 
			
		||||
            isAuthenticated ? logout() : login("/");
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {isAuthenticated ? `logout (${accessTokenPayload.email})` : "login"}
 | 
			
		||||
        </button>
 | 
			
		||||
      </p>
 | 
			
		||||
 | 
			
		||||
      {/* This translation uses a formatter in the translation files  */}
 | 
			
		||||
      <p>{translate("navBar.currentDate", { date: DateTime.now().toJSDate() })}</p>
 | 
			
		||||
      <nav data-testid="nav">
 | 
			
		||||
        <Link to="/" data-testid="nav.home">
 | 
			
		||||
        <Link to={home} data-testid="nav.home">
 | 
			
		||||
          {translate("nav.home")}
 | 
			
		||||
        </Link>
 | 
			
		||||
        <Link to="/about" data-testid="nav.about">
 | 
			
		||||
        <Link to={about} data-testid="nav.about">
 | 
			
		||||
          {translate("nav.about")}
 | 
			
		||||
        </Link>
 | 
			
		||||
        <Link to="/counter" data-testid="nav.counter">
 | 
			
		||||
        <Link to={counter} data-testid="nav.counter">
 | 
			
		||||
          {translate("nav.counter")}
 | 
			
		||||
        </Link>
 | 
			
		||||
        <Link to="/tenders" data-testid="nav.tenders">
 | 
			
		||||
          {translate("nav.tenders")}
 | 
			
		||||
        </Link>
 | 
			
		||||
        <button onClick={() => i18n.changeLanguage("en")}>en</button>
 | 
			
		||||
        <button onClick={() => i18n.changeLanguage("nl")}>nl</button>
 | 
			
		||||
        <hr />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								src/infrastructure/sso/Configuration.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/infrastructure/sso/Configuration.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
import { OidcConfiguration } from "@axa-fr/react-oidc/dist/vanilla/oidc";
 | 
			
		||||
import { Config } from "../config";
 | 
			
		||||
 | 
			
		||||
const { clientId, redirectUri, silentRedirectUri, scope, url } = Config.oidc;
 | 
			
		||||
 | 
			
		||||
/* eslint-disable camelcase */
 | 
			
		||||
export const SSOConfiguration: OidcConfiguration = {
 | 
			
		||||
  client_id: clientId,
 | 
			
		||||
  redirect_uri: redirectUri,
 | 
			
		||||
  silent_redirect_uri: silentRedirectUri,
 | 
			
		||||
  scope,
 | 
			
		||||
  authority: url,
 | 
			
		||||
  service_worker_only: false,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										28
									
								
								src/infrastructure/sso/OidcProvider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/infrastructure/sso/OidcProvider.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
import { OidcProvider as ReactOidcProvider } from "@axa-fr/react-oidc";
 | 
			
		||||
import { FunctionComponent, ReactNode } from "react";
 | 
			
		||||
import { AppLoader } from "../loader/appLoader";
 | 
			
		||||
import { SSOConfiguration } from "./Configuration";
 | 
			
		||||
import { Authenticating } from "./overrides/Authenticating";
 | 
			
		||||
import { AuthenticatingError } from "./overrides/AuthenticatingError";
 | 
			
		||||
import { CallBackSuccess } from "./overrides/CallBackSuccess";
 | 
			
		||||
import { ServiceWorkerNotSupported } from "./overrides/ServiceWorkerNotSupported";
 | 
			
		||||
import { SessionLost } from "./overrides/SessionLost";
 | 
			
		||||
 | 
			
		||||
type Props = { children?: ReactNode; [x: string]: any };
 | 
			
		||||
 | 
			
		||||
export const OidcProvider: FunctionComponent<Props> = ({ children, ...rest }) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <ReactOidcProvider
 | 
			
		||||
      loadingComponent={AppLoader}
 | 
			
		||||
      authenticatingErrorComponent={AuthenticatingError}
 | 
			
		||||
      authenticatingComponent={Authenticating}
 | 
			
		||||
      sessionLostComponent={SessionLost}
 | 
			
		||||
      serviceWorkerNotSupportedComponent={ServiceWorkerNotSupported}
 | 
			
		||||
      callbackSuccessComponent={CallBackSuccess}
 | 
			
		||||
      configuration={SSOConfiguration}
 | 
			
		||||
      {...rest}
 | 
			
		||||
    >
 | 
			
		||||
      {children}
 | 
			
		||||
    </ReactOidcProvider>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										31
									
								
								src/infrastructure/sso/models/OIDCConfig.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/infrastructure/sso/models/OIDCConfig.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
export interface OIDCConfig {
 | 
			
		||||
  /**
 | 
			
		||||
   * Authority URL
 | 
			
		||||
   */
 | 
			
		||||
  url: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Realm to authenticate against
 | 
			
		||||
   */
 | 
			
		||||
  realm: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Id of this client
 | 
			
		||||
   */
 | 
			
		||||
  clientId: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Scope(s) that you want to request
 | 
			
		||||
   */
 | 
			
		||||
  scope: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * URI to redirect to after successful login
 | 
			
		||||
   */
 | 
			
		||||
  redirectUri: string;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * redirect uri for the silent refresh
 | 
			
		||||
   */
 | 
			
		||||
  silentRedirectUri: string;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/infrastructure/sso/models/SSOResult.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/infrastructure/sso/models/SSOResult.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
export interface SSOResult {
 | 
			
		||||
  configurationName: string;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/infrastructure/sso/overrides/Authenticating.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/infrastructure/sso/overrides/Authenticating.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { FunctionComponent } from "react";
 | 
			
		||||
import { SSOResult } from "../models/SSOResult";
 | 
			
		||||
 | 
			
		||||
export const Authenticating: FunctionComponent<SSOResult> = ({ configurationName }) => (
 | 
			
		||||
  <>
 | 
			
		||||
    <h1>Authentication in progress for {configurationName}</h1>
 | 
			
		||||
    <p>You will be redirected to the login page.</p>
 | 
			
		||||
  </>
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										9
									
								
								src/infrastructure/sso/overrides/AuthenticatingError.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/infrastructure/sso/overrides/AuthenticatingError.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { FunctionComponent } from "react";
 | 
			
		||||
import { SSOResult } from "../models/SSOResult";
 | 
			
		||||
 | 
			
		||||
export const AuthenticatingError: FunctionComponent<SSOResult> = ({ configurationName }) => (
 | 
			
		||||
  <>
 | 
			
		||||
    <h1>Error for {configurationName}</h1>
 | 
			
		||||
    <p>An error occurred during authentication.</p>
 | 
			
		||||
  </>
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										9
									
								
								src/infrastructure/sso/overrides/CallBackSuccess.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/infrastructure/sso/overrides/CallBackSuccess.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { FunctionComponent } from "react";
 | 
			
		||||
import { SSOResult } from "../models/SSOResult";
 | 
			
		||||
 | 
			
		||||
export const CallBackSuccess: FunctionComponent<SSOResult> = ({ configurationName }) => (
 | 
			
		||||
  <>
 | 
			
		||||
    <h1>Authentication complete for {configurationName}</h1>
 | 
			
		||||
    <p>You will be redirected...</p>
 | 
			
		||||
  </>
 | 
			
		||||
);
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
import { FunctionComponent } from "react";
 | 
			
		||||
import { SSOResult } from "../models/SSOResult";
 | 
			
		||||
 | 
			
		||||
export const ServiceWorkerNotSupported: FunctionComponent<SSOResult> = ({ configurationName }) => (
 | 
			
		||||
  <>
 | 
			
		||||
    <h1>Unable to authenticate on this browser for {configurationName}</h1>
 | 
			
		||||
    <p>Your browser is not configured to support Service Workers.</p>
 | 
			
		||||
  </>
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										17
									
								
								src/infrastructure/sso/overrides/SessionLost.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/infrastructure/sso/overrides/SessionLost.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import { useOidc } from "@axa-fr/react-oidc";
 | 
			
		||||
import { FunctionComponent } from "react";
 | 
			
		||||
import { SSOResult } from "../models/SSOResult";
 | 
			
		||||
 | 
			
		||||
export const SessionLost: FunctionComponent<SSOResult> = ({ configurationName }) => {
 | 
			
		||||
  const { login } = useOidc(configurationName);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <h1>Session timed out for {configurationName}</h1>
 | 
			
		||||
      <p>Your session has expired. Please re-authenticate.</p>
 | 
			
		||||
      <button type="button" onClick={() => login("/")}>
 | 
			
		||||
        Login
 | 
			
		||||
      </button>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										28
									
								
								src/setupProxy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/setupProxy.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
const { createProxyMiddleware } = require("http-proxy-middleware");
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a simple proxy that automatically removes the prefix endpoint
 | 
			
		||||
 * @param {*} app app to configure proxy on
 | 
			
		||||
 * @param {*} endpoint endpoint you want to use for the proxy
 | 
			
		||||
 * @param {*} target proxy target
 | 
			
		||||
 */
 | 
			
		||||
const createSimpleProxy = (app, endpoint, target) => {
 | 
			
		||||
  app.use(
 | 
			
		||||
    endpoint,
 | 
			
		||||
    createProxyMiddleware({
 | 
			
		||||
      target,
 | 
			
		||||
      changeOrigin: true,
 | 
			
		||||
      pathRewrite: {
 | 
			
		||||
        [`^${endpoint}`]: "",
 | 
			
		||||
      },
 | 
			
		||||
    }),
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Actual proxy configuration
 | 
			
		||||
 * @param {*} app app to configure proxy on
 | 
			
		||||
 */
 | 
			
		||||
module.exports = function (app) {
 | 
			
		||||
  createSimpleProxy(app, "/tendersguru", "https://tenders.guru");
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user