From d41e9d3af4b7f3d2da72dad0ebbe70c147f55bb2 Mon Sep 17 00:00:00 2001
From: Mastermindzh
Date: Tue, 19 Jul 2022 11:17:38 +0200
Subject: [PATCH 1/3] forking outside of github instructions
---
README.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/README.md b/README.md
index 004059a..e4a129b 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ Includes:
- [Getting started](#getting-started)
- [Project structure](#project-structure)
+- ["Forking" outside of Github](#forking-outside-of-github)
@@ -48,3 +49,14 @@ Only the important files are shown
├── README.md # keep this up to date
└── 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 `
+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
From 8496f5cfbe6d4a1d301430a8392c847af636760e Mon Sep 17 00:00:00 2001
From: Mastermindzh
Date: Tue, 26 Jul 2022 10:32:11 +0200
Subject: [PATCH 2/3] Moved examples into example directory
Moved routes to separate file
Used route constants
---
CHANGELOG.md | 6 ++++
package-lock.json | 4 +--
package.json | 2 +-
src/App.tsx | 19 ++----------
src/Routes.tsx | 30 +++++++++++++++++++
src/app/store.ts | 2 +-
.../tests/mocks/i18n/WithTestTranslations.tsx | 9 +++++-
src/features/counter/services/counterAPI.ts | 4 ---
.../{ => examples}/counter/Counter.module.css | 4 +--
.../{ => examples}/counter/Counter.tsx | 2 +-
.../counter/models/CounterState.ts | 0
.../examples/counter/services/counterAPI.ts | 6 ++++
.../counter/state/actions/incrementAsync.ts | 0
.../counter/state/counterSlice.spec.ts | 6 +---
.../counter/state/counterSlice.ts | 2 +-
src/features/home/Home.tsx | 28 ++++++++++++++---
src/infrastructure/config/config.ts | 3 +-
src/infrastructure/navbar/Navbar.tsx | 8 +++--
18 files changed, 93 insertions(+), 42 deletions(-)
create mode 100644 src/Routes.tsx
delete mode 100644 src/features/counter/services/counterAPI.ts
rename src/features/{ => examples}/counter/Counter.module.css (94%)
rename src/features/{ => examples}/counter/Counter.tsx (96%)
rename src/features/{ => examples}/counter/models/CounterState.ts (100%)
create mode 100644 src/features/examples/counter/services/counterAPI.ts
rename src/features/{ => examples}/counter/state/actions/incrementAsync.ts (100%)
rename src/features/{ => examples}/counter/state/counterSlice.spec.ts (89%)
rename src/features/{ => examples}/counter/state/counterSlice.ts (97%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8de336c..b675478 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ 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.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
diff --git a/package-lock.json b/package-lock.json
index 65f9319..eef3d90 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"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": {
diff --git a/package.json b/package.json
index 68a3c2b..85d48be 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-starter-kit",
- "version": "0.4.0",
+ "version": "0.4.1",
"description": "A modern, create-react-app-based, starter kit for React projects",
"keywords": [
"react",
diff --git a/src/App.tsx b/src/App.tsx
index fe59fdc..7ffe30a 100644
--- a/src/App.tsx
+++ b/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 (
-
- } />
- } />
- } />
- {/* } /> */}
- {/* }>
- } />
- } />
- } />
- */}
- {/* */}
-
+
);
diff --git a/src/Routes.tsx b/src/Routes.tsx
new file mode 100644
index 0000000..5ba45b4
--- /dev/null
+++ b/src/Routes.tsx
@@ -0,0 +1,30 @@
+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 { HomeContainer } from "./features/home/Home";
+type Props = {};
+
+export const ROUTE_KEYS = {
+ home: "",
+ about: "about",
+ counter: "counter",
+};
+
+export const AppRoutes: FunctionComponent = () => {
+ const { home, about, counter } = ROUTE_KEYS;
+ return (
+
+ } />
+ } />
+ } />
+ {/* } /> */}
+ {/* }>
+ } />
+ } />
+ } />
+ */}
+ {/* */}
+
+ );
+};
diff --git a/src/app/store.ts b/src/app/store.ts
index 8219d1b..370bd49 100644
--- a/src/app/store.ts
+++ b/src/app/store.ts
@@ -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: {
diff --git a/src/app/tests/mocks/i18n/WithTestTranslations.tsx b/src/app/tests/mocks/i18n/WithTestTranslations.tsx
index 2b31c9c..970301a 100644
--- a/src/app/tests/mocks/i18n/WithTestTranslations.tsx
+++ b/src/app/tests/mocks/i18n/WithTestTranslations.tsx
@@ -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 = ({ children, keysOnly }) => {
const [_translate, i18nSettings] = useTranslation();
diff --git a/src/features/counter/services/counterAPI.ts b/src/features/counter/services/counterAPI.ts
deleted file mode 100644
index dc76027..0000000
--- a/src/features/counter/services/counterAPI.ts
+++ /dev/null
@@ -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));
-}
diff --git a/src/features/counter/Counter.module.css b/src/features/examples/counter/Counter.module.css
similarity index 94%
rename from src/features/counter/Counter.module.css
rename to src/features/examples/counter/Counter.module.css
index 025bb72..ce3e2d3 100644
--- a/src/features/counter/Counter.module.css
+++ b/src/features/examples/counter/Counter.module.css
@@ -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;
diff --git a/src/features/counter/Counter.tsx b/src/features/examples/counter/Counter.tsx
similarity index 96%
rename from src/features/counter/Counter.tsx
rename to src/features/examples/counter/Counter.tsx
index c2642d5..108a6a5 100644
--- a/src/features/counter/Counter.tsx
+++ b/src/features/examples/counter/Counter.tsx
@@ -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 {
diff --git a/src/features/counter/models/CounterState.ts b/src/features/examples/counter/models/CounterState.ts
similarity index 100%
rename from src/features/counter/models/CounterState.ts
rename to src/features/examples/counter/models/CounterState.ts
diff --git a/src/features/examples/counter/services/counterAPI.ts b/src/features/examples/counter/services/counterAPI.ts
new file mode 100644
index 0000000..547bb9c
--- /dev/null
+++ b/src/features/examples/counter/services/counterAPI.ts
@@ -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),
+ );
+}
diff --git a/src/features/counter/state/actions/incrementAsync.ts b/src/features/examples/counter/state/actions/incrementAsync.ts
similarity index 100%
rename from src/features/counter/state/actions/incrementAsync.ts
rename to src/features/examples/counter/state/actions/incrementAsync.ts
diff --git a/src/features/counter/state/counterSlice.spec.ts b/src/features/examples/counter/state/counterSlice.spec.ts
similarity index 89%
rename from src/features/counter/state/counterSlice.spec.ts
rename to src/features/examples/counter/state/counterSlice.spec.ts
index c823746..8f69b40 100644
--- a/src/features/counter/state/counterSlice.spec.ts
+++ b/src/features/examples/counter/state/counterSlice.spec.ts
@@ -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", () => {
diff --git a/src/features/counter/state/counterSlice.ts b/src/features/examples/counter/state/counterSlice.ts
similarity index 97%
rename from src/features/counter/state/counterSlice.ts
rename to src/features/examples/counter/state/counterSlice.ts
index 5086b7b..5905f05 100644
--- a/src/features/counter/state/counterSlice.ts
+++ b/src/features/examples/counter/state/counterSlice.ts
@@ -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";
diff --git a/src/features/home/Home.tsx b/src/features/home/Home.tsx
index 1ace099..c1be04c 100644
--- a/src/features/home/Home.tsx
+++ b/src/features/home/Home.tsx
@@ -8,19 +8,39 @@ export const HomeContainer: FunctionComponent = () => {
This is the react base app :)
Learn
-
+
React
,
-
+
Redux
,
-
+
Redux Toolkit
, and
-
+
React Redux
diff --git a/src/infrastructure/config/config.ts b/src/infrastructure/config/config.ts
index d7b5046..f0faf1f 100644
--- a/src/infrastructure/config/config.ts
+++ b/src/infrastructure/config/config.ts
@@ -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;
diff --git a/src/infrastructure/navbar/Navbar.tsx b/src/infrastructure/navbar/Navbar.tsx
index 5e6f1c7..dff7bd9 100644
--- a/src/infrastructure/navbar/Navbar.tsx
+++ b/src/infrastructure/navbar/Navbar.tsx
@@ -2,12 +2,14 @@ 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 = () => {
const [translate, i18n] = useTranslation();
+ const { home, about, counter } = ROUTE_KEYS;
return (
<>
{translate("navBar.intro")}
@@ -20,13 +22,13 @@ export const Navbar: FunctionComponent = () => {
{/* This translation uses a formatter in the translation files */}
{translate("navBar.currentDate", { date: DateTime.now().toJSDate() })}
{/* This translation uses a formatter in the translation files */}
@@ -31,6 +41,9 @@ export const Navbar: FunctionComponent = () => {
{translate("nav.counter")}
+
+ {translate("nav.tenders")}
+
diff --git a/src/infrastructure/sso/Configuration.ts b/src/infrastructure/sso/Configuration.ts
new file mode 100644
index 0000000..57125a9
--- /dev/null
+++ b/src/infrastructure/sso/Configuration.ts
@@ -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,
+};
diff --git a/src/infrastructure/sso/OidcProvider.tsx b/src/infrastructure/sso/OidcProvider.tsx
new file mode 100644
index 0000000..ff1fbdf
--- /dev/null
+++ b/src/infrastructure/sso/OidcProvider.tsx
@@ -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 = ({ children, ...rest }) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/infrastructure/sso/models/OIDCConfig.tsx b/src/infrastructure/sso/models/OIDCConfig.tsx
new file mode 100644
index 0000000..3e02433
--- /dev/null
+++ b/src/infrastructure/sso/models/OIDCConfig.tsx
@@ -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;
+}
diff --git a/src/infrastructure/sso/models/SSOResult.tsx b/src/infrastructure/sso/models/SSOResult.tsx
new file mode 100644
index 0000000..830f66f
--- /dev/null
+++ b/src/infrastructure/sso/models/SSOResult.tsx
@@ -0,0 +1,3 @@
+export interface SSOResult {
+ configurationName: string;
+}
diff --git a/src/infrastructure/sso/overrides/Authenticating.tsx b/src/infrastructure/sso/overrides/Authenticating.tsx
new file mode 100644
index 0000000..54bd54a
--- /dev/null
+++ b/src/infrastructure/sso/overrides/Authenticating.tsx
@@ -0,0 +1,9 @@
+import { FunctionComponent } from "react";
+import { SSOResult } from "../models/SSOResult";
+
+export const Authenticating: FunctionComponent = ({ configurationName }) => (
+ <>
+ Authentication in progress for {configurationName}
+ You will be redirected to the login page.
+ >
+);
diff --git a/src/infrastructure/sso/overrides/AuthenticatingError.tsx b/src/infrastructure/sso/overrides/AuthenticatingError.tsx
new file mode 100644
index 0000000..bd8f7bd
--- /dev/null
+++ b/src/infrastructure/sso/overrides/AuthenticatingError.tsx
@@ -0,0 +1,9 @@
+import { FunctionComponent } from "react";
+import { SSOResult } from "../models/SSOResult";
+
+export const AuthenticatingError: FunctionComponent = ({ configurationName }) => (
+ <>
+ Error for {configurationName}
+ An error occurred during authentication.
+ >
+);
diff --git a/src/infrastructure/sso/overrides/CallBackSuccess.tsx b/src/infrastructure/sso/overrides/CallBackSuccess.tsx
new file mode 100644
index 0000000..9b52df3
--- /dev/null
+++ b/src/infrastructure/sso/overrides/CallBackSuccess.tsx
@@ -0,0 +1,9 @@
+import { FunctionComponent } from "react";
+import { SSOResult } from "../models/SSOResult";
+
+export const CallBackSuccess: FunctionComponent = ({ configurationName }) => (
+ <>
+ Authentication complete for {configurationName}
+ You will be redirected...
+ >
+);
diff --git a/src/infrastructure/sso/overrides/ServiceWorkerNotSupported.tsx b/src/infrastructure/sso/overrides/ServiceWorkerNotSupported.tsx
new file mode 100644
index 0000000..1bf88bb
--- /dev/null
+++ b/src/infrastructure/sso/overrides/ServiceWorkerNotSupported.tsx
@@ -0,0 +1,9 @@
+import { FunctionComponent } from "react";
+import { SSOResult } from "../models/SSOResult";
+
+export const ServiceWorkerNotSupported: FunctionComponent = ({ configurationName }) => (
+ <>
+ Unable to authenticate on this browser for {configurationName}
+ Your browser is not configured to support Service Workers.
+ >
+);
diff --git a/src/infrastructure/sso/overrides/SessionLost.tsx b/src/infrastructure/sso/overrides/SessionLost.tsx
new file mode 100644
index 0000000..5cb53a7
--- /dev/null
+++ b/src/infrastructure/sso/overrides/SessionLost.tsx
@@ -0,0 +1,17 @@
+import { useOidc } from "@axa-fr/react-oidc";
+import { FunctionComponent } from "react";
+import { SSOResult } from "../models/SSOResult";
+
+export const SessionLost: FunctionComponent = ({ configurationName }) => {
+ const { login } = useOidc(configurationName);
+
+ return (
+ <>
+ Session timed out for {configurationName}
+ Your session has expired. Please re-authenticate.
+
+ >
+ );
+};
diff --git a/src/setupProxy.js b/src/setupProxy.js
new file mode 100644
index 0000000..31dafb5
--- /dev/null
+++ b/src/setupProxy.js
@@ -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");
+};