├── .dockerignore
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── cypress.json
├── cypress
├── fixtures
│ └── example.json
├── integration
│ ├── header.spec.js
│ └── init.spec.js
├── plugins
│ └── index.js
└── support
│ ├── commands.js
│ └── index.js
├── lerna.json
├── package.json
├── packages
├── core-ui
│ ├── .env.development
│ ├── .env.production
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── icon-128x128.png
│ │ ├── icon-144x144.png
│ │ ├── icon-152x152.png
│ │ ├── icon-192x192.png
│ │ ├── icon-384x384.png
│ │ ├── icon-512x512.png
│ │ ├── icon-72x72.png
│ │ ├── icon-96x96.png
│ │ ├── index.html
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── GlobalStyles.ts
│ │ ├── Routes
│ │ │ ├── Routes.tsx
│ │ │ └── index.tsx
│ │ ├── ThemeProvider.tsx
│ │ ├── actions-types
│ │ │ └── index.ts
│ │ ├── actions
│ │ │ ├── app.ts
│ │ │ └── counter.ts
│ │ ├── assets
│ │ │ └── images
│ │ │ │ ├── hero-bg.png
│ │ │ │ ├── pizza.png
│ │ │ │ └── salmon.png
│ │ ├── components
│ │ │ ├── Counter.tsx
│ │ │ ├── ErrorBoundary.tsx
│ │ │ ├── Footer
│ │ │ │ ├── Footer.tsx
│ │ │ │ ├── Styled.ts
│ │ │ │ └── index.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── Hero
│ │ │ │ ├── Hero.tsx
│ │ │ │ ├── Styled.ts
│ │ │ │ └── index.tsx
│ │ │ ├── Loader
│ │ │ │ ├── Styled.ts
│ │ │ │ └── index.tsx
│ │ │ ├── Navbar
│ │ │ │ ├── Navbar.tsx
│ │ │ │ ├── Styled.ts
│ │ │ │ └── index.tsx
│ │ │ ├── NotFound
│ │ │ │ ├── NotFound.tsx
│ │ │ │ ├── Styled.ts
│ │ │ │ └── index.tsx
│ │ │ ├── Recipes
│ │ │ │ ├── Recipes.tsx
│ │ │ │ ├── Styled.ts
│ │ │ │ └── index.tsx
│ │ │ ├── ToggleSwitch
│ │ │ │ ├── Styled.ts
│ │ │ │ ├── ToggleSwitch.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── Welcome
│ │ │ │ ├── Styled.ts
│ │ │ │ ├── Welcome.tsx
│ │ │ │ └── index.tsx
│ │ │ └── Works
│ │ │ │ ├── Styled.ts
│ │ │ │ ├── Works.tsx
│ │ │ │ └── index.tsx
│ │ ├── config
│ │ │ ├── env.ts
│ │ │ ├── host.ts
│ │ │ ├── images.ts
│ │ │ ├── routes.ts
│ │ │ └── routes_config.ts
│ │ ├── index.tsx
│ │ ├── micro-frontend
│ │ │ ├── FeatureOne.tsx
│ │ │ ├── MicroFrontend.tsx
│ │ │ └── Styled.ts
│ │ ├── pages
│ │ │ ├── About.tsx
│ │ │ ├── Home.tsx
│ │ │ ├── NotFound.tsx
│ │ │ └── Recipes.tsx
│ │ ├── react-app-env.d.ts
│ │ ├── reducers
│ │ │ ├── app.ts
│ │ │ ├── counter.ts
│ │ │ └── index.ts
│ │ ├── reportWebVitals.ts
│ │ ├── scss
│ │ │ ├── App.scss
│ │ │ └── index.scss
│ │ ├── service-worker.ts
│ │ ├── serviceWorkerRegistration.ts
│ │ ├── setupTests.ts
│ │ └── store
│ │ │ ├── configureStore.dev.ts
│ │ │ ├── configureStore.prod.ts
│ │ │ └── index.ts
│ └── tsconfig.json
├── frontend-components
│ └── feature-01
│ │ ├── .babelrc
│ │ ├── .env.development
│ │ ├── .env.production
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ │ ├── src
│ │ ├── App.tsx
│ │ ├── Routes
│ │ │ └── Routes.tsx
│ │ ├── StyleProvider.tsx
│ │ ├── actions-types
│ │ │ └── index.ts
│ │ ├── actions
│ │ │ └── alerts.ts
│ │ ├── assets
│ │ │ └── images
│ │ │ │ └── Big-chicken.svg
│ │ ├── components
│ │ │ ├── Alerts.tsx
│ │ │ ├── Order.tsx
│ │ │ ├── RecipeDetails
│ │ │ │ ├── RecipeDetails.tsx
│ │ │ │ ├── Styled.ts
│ │ │ │ └── index.tsx
│ │ │ └── WishList.tsx
│ │ ├── config
│ │ │ ├── host.ts
│ │ │ └── images.ts
│ │ ├── index.tsx
│ │ ├── react-app-env.d.ts
│ │ ├── reducers
│ │ │ ├── alerts.ts
│ │ │ └── index.ts
│ │ ├── reportWebVitals.ts
│ │ ├── scss
│ │ │ ├── App.scss
│ │ │ └── index.scss
│ │ ├── setupTests.ts
│ │ └── store
│ │ │ ├── configureStore.dev.ts
│ │ │ ├── configureStore.prod.ts
│ │ │ └── index.ts
│ │ └── tsconfig.json
└── shared-components
│ ├── .babelrc
│ ├── .env
│ ├── .gitignore
│ ├── .storybook
│ └── main.js
│ ├── README.md
│ ├── package.json
│ ├── rollup.config.ts
│ ├── src
│ ├── bundle.ts
│ ├── components
│ │ ├── Button
│ │ │ ├── Button.test.tsx
│ │ │ ├── Button.tsx
│ │ │ └── index.tsx
│ │ ├── Container
│ │ │ └── index.tsx
│ │ └── OutlineButton
│ │ │ └── index.tsx
│ ├── react-app-env.d.ts
│ ├── scss
│ │ ├── App.scss
│ │ └── index.scss
│ ├── services
│ │ ├── HttpService.ts
│ │ └── Toast.ts
│ ├── setupTests.ts
│ ├── stories
│ │ ├── 0-Welcome.stories.tsx
│ │ └── 1-Button.stories.tsx
│ └── utils
│ │ └── commonFunction.ts
│ └── tsconfig.json
├── tsconfig.json
└── yarn.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | npm-debug.log
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: CI
4 |
5 | # Controls when the workflow will run
6 | on:
7 | # Triggers the workflow on push or pull request events but only for the master branch
8 | push:
9 | branches: [ master ]
10 | pull_request:
11 | branches: [ master ]
12 |
13 | # Allows you to run this workflow manually from the Actions tab
14 | workflow_dispatch:
15 |
16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
17 | jobs:
18 | # This workflow contains a single job called "build"
19 | build:
20 | # The type of runner that the job will run on
21 | runs-on: ubuntu-latest
22 |
23 | # Steps represent a sequence of tasks that will be executed as part of the job
24 | steps:
25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
26 | - uses: actions/checkout@v2
27 | - name: Configure AWS Credentials
28 | uses: aws-actions/configure-aws-credentials@v1
29 | with:
30 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
31 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
32 | aws-region: ${{ secrets.AWS_REGION }}
33 | - name: Install dependencies
34 | run: yarn
35 | - name: Linking packages
36 | run: yarn bootstrap
37 | - name: Make a build
38 | run: yarn build
39 | - name: Copy feature-one in core-ui build folder
40 | run : cp -r ./packages/frontend-components/feature-01/build ./packages/core-ui/build/feature-one
41 | - name: Deploy app build to S3 bucket
42 | run: aws s3 sync ./packages/core-ui/build/ s3://my-react-mfe --delete
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /node_modules/*
6 | /.pnp/*
7 | /.pnp.js
8 |
9 | # testing
10 | /coverage/*
11 | /junit.xml
12 |
13 | # production
14 | /build/*
15 | /dist/*
16 |
17 | # misc
18 | .DS_Store
19 | .idea
20 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute?
2 | - 1 Fork the project.
3 | - 2 Make required changes and commit.
4 | - 3 Generate pull request. Mention all the required description regarding changes you made.
5 |
6 | Happy coding. 🙂
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # build environment
2 | FROM node:14.17.3
3 |
4 | # ARG FROM_IMAGE=react-mfe
5 | # ARG FROM_TAG=latest
6 | # FROM ${FROM_IMAGE}:${FROM_TAG} AS react-mfe-build
7 | ARG SRCDIR=/usr/src/app
8 |
9 | # set working directory
10 | WORKDIR ${SRCDIR}
11 |
12 | # Install app dependencies
13 | COPY package.json ${SRCDIR}/package.json
14 | COPY lerna.json ${SRCDIR}/lerna.json
15 | COPY yarn.lock ${SRCDIR}/yarn.lock
16 | COPY ./packages/core-ui/package.json ${SRCDIR}/packages/core-ui/
17 | COPY ./packages/frontend-components/feature-01/package.json ${SRCDIR}/packages/frontend-components/feature-01/
18 | COPY ./packages/shared-components/package.json ${SRCDIR}/packages/shared-components/
19 |
20 | RUN yarn global add serve
21 |
22 | # RUN install dependencies
23 | RUN yarn
24 |
25 | # Bundle app source
26 | COPY . ${SRCDIR}
27 | RUN yarn lerna bootstrap
28 | RUN yarn lerna run build
29 |
30 | COPY ./packages/frontend-components/feature-01/build ${SRCDIR}/packages/core-ui/build/feature-one
31 |
32 | # Expose the port and start the application
33 | EXPOSE 9090
34 |
35 | # CMD [ "yarn", "workspace", "@mfe/core-ui", "run", "start" ]
36 | CMD [ "serve", "-s", "/usr/src/app/packages/core-ui/build"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Asif Vora
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
React-mfe ✨️
2 | A micro frontend implementation for react js
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | [](https://reactjs.org/)
22 | [](https://redux.js.org/)
23 | [](https://www.typescriptlang.org/)
24 | [](https://reactrouter.com/)
25 | [](https://storybook.js.org/)
26 | [](https://lerna.js.org/)
27 | [](https://github.com/styled-components/styled-components)
28 | [](https://www.cypress.io/)
29 |
30 |
31 |
32 |
33 | # Why?
34 | ### To scale with multiple teams in a micro services environment
35 |
36 | - More cohesive codebase
37 | - Simplify maintenance
38 | - Allows to scale development teams
39 | - Simplify updates
40 | - Independent deploy
41 |
42 | # Read more about Micro frontends
43 |
44 |
45 | # 📖 App architecture
46 |
47 | - packages
48 | - core-ui
49 | - frontend-components
50 | - feature-01
51 | - feature-02
52 | - feature-03
53 | - feature-04
54 | - feature-05
55 | - ...
56 | - shared-components
57 |
58 | # 💻 Built With
59 | - [Lerna 🐉](https://lerna.js.org/)
60 | - [React](https://reactjs.org/)
61 | - [Redux](https://redux.js.org/)
62 | - [TypeScript](https://www.typescriptlang.org/)
63 | - [Rollup](https://rollupjs.org/)
64 | - [Styled Component](https://styled-components.com/)
65 | - [SCSS](https://sass-lang.com/)
66 | - [Storybook](https://storybook.js.org/)
67 | - [Router](https://reactrouter.com/)
68 | - Some services and utils
69 |
70 | # 🛠️ Installation Steps
71 |
72 | ### Clone the repository ⎘
73 | ```bash
74 | git clone https://github.com/asifvora/react-mfe.git
75 |
76 | ```
77 |
78 | ### Change the working directory 📂
79 | ```bash
80 | cd react-mfe
81 | ```
82 |
83 | ### Install dependency 🚚
84 | ```bash
85 | yarn
86 | ```
87 |
88 | ### Linking dependencies... 🔗
89 | ```bash
90 | yarn bootstrap
91 | ```
92 |
93 | ### Start core-ui app :rocket:
94 | ```bash
95 | yarn start:core-ui
96 | ```
97 |
98 | ### Start feature-01 app :rocket:
99 | ```bash
100 | yarn start:feature-01
101 | ```
102 |
103 | ### Build all packages 🔨
104 | ```bash
105 | yarn build
106 | ```
107 |
108 | You are all set! Open [localhost:3000](http://localhost:3000/) to see the app.
109 |
110 |
111 | # :whale: Docker
112 | ### Build
113 | ```bash
114 | docker build -t
115 | docker build -t react-mfe .
116 | docker image ls
117 | ```
118 |
119 | ### Run a Container
120 | ```bash
121 | docker run -d -p :
122 | docker run -d -p 9090:5000 react-mfe
123 |
124 | ```
125 | ### Check the running container
126 | ```bash
127 | docker ps -a
128 | ```
129 |
130 | # 🛡️ License
131 |
132 | This project is licensed under the MIT License - see the [`LICENSE`](LICENSE) file for details.
133 |
134 | # 👨💻 Author
135 | ### 👤 Asif Vora
136 | - Github: [@asifvora](https://github.com/asifvora)
137 | - LinkedIn: [@asif-vora](https://www.linkedin.com/in/asif-vora/)
138 | - Twitter: [@007_dark_shadow](https://twitter.com/007_dark_shadow)
139 | - Instagram: [@007_dark_shadow](https://www.instagram.com/007_dark_shadow/)
140 |
141 | # 🍰 Contributing
142 |
143 | - Please contribute using [GitHub Flow](https://guides.github.com/introduction/flow). Create a branch, add commits, and [open a pull request](https://github.com/asifvora/react-mfe/compare).
144 |
145 | - Please read [`CONTRIBUTING`](CONTRIBUTING.md) for details.
146 |
147 | # 🙏 Support
148 | This project needs a ⭐️ from you. Don't forget to leave a star ⭐️
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:3000"
3 | }
--------------------------------------------------------------------------------
/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/cypress/integration/header.spec.js:
--------------------------------------------------------------------------------
1 | describe("Header", () => {
2 | beforeEach(() => {
3 | cy.visit("/");
4 | });
5 |
6 | it("render Food HQ link in header", () => {
7 | cy.get("a").contains("Food HQ");
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/cypress/integration/init.spec.js:
--------------------------------------------------------------------------------
1 | describe("Cypress", () => {
2 | it("is working", () => {
3 | expect(true).to.equal(true);
4 | });
5 |
6 | it("visits the app", () => {
7 | cy.visit("/");
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 |
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | module.exports = (on, config) => {
16 | // `on` is used to hook into various events Cypress emits
17 | // `config` is the resolved Cypress config
18 | }
--------------------------------------------------------------------------------
/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/core-ui",
4 | "packages/frontend-components/*",
5 | "packages/shared-components"
6 | ],
7 | "version": "0.0.0",
8 | "npmClient": "yarn",
9 | "useWorkspaces": true
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "workspaces": {
4 | "packages": [
5 | "packages/core-ui",
6 | "packages/frontend-components/*",
7 | "packages/shared-components"
8 | ]
9 | },
10 | "devDependencies": {
11 | "@types/react": "^17.0.5",
12 | "@types/react-dom": "^17.0.3",
13 | "@types/react-redux": "^7.1.16",
14 | "@types/react-router-dom": "^5.1.7",
15 | "@types/redux-logger": "^3.0.8",
16 | "axios": "^0.21.1",
17 | "lerna": "^4.0.0",
18 | "react": "^17.0.2",
19 | "react-dom": "^17.0.2",
20 | "react-redux": "^7.2.4",
21 | "react-router-dom": "^5.2.0",
22 | "react-scripts": "4.0.1",
23 | "react-toastify": "^7.0.4",
24 | "redux": "^4.1.0",
25 | "redux-devtools-extension": "^2.13.9",
26 | "redux-logger": "^3.0.6",
27 | "redux-thunk": "^2.3.0",
28 | "react-icons": "^4.2.0"
29 | },
30 | "scripts": {
31 | "build": "yarn lerna run build --ignore=@mfe/shared",
32 | "prebuild": "yarn lerna run build --scope=@mfe/shared",
33 | "start:core-ui": "yarn workspace @mfe/core-ui start",
34 | "start:feature-01": "yarn workspace @mfe/feature-01 start",
35 | "link": "yarn lerna run link",
36 | "clean": "yarn lerna run clean",
37 | "bootstrap": "yarn lerna bootstrap",
38 | "cypress": "cypress open"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/core-ui/.env.development:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=true
2 | SKIP_PREFLIGHT_CHECK=true
3 | REACT_APP_FEATURE_ONE_HOST=http://localhost:9692
4 |
--------------------------------------------------------------------------------
/packages/core-ui/.env.production:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | SKIP_PREFLIGHT_CHECK=true
3 | REACT_APP_FEATURE_ONE_HOST=/feature-one
--------------------------------------------------------------------------------
/packages/core-ui/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | #cache
26 | .eslintcache
--------------------------------------------------------------------------------
/packages/core-ui/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/packages/core-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mfe/core-ui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "author": "asifvora",
6 | "license": "MIT",
7 | "peerDependencies": {
8 | "react": "^17.0.2",
9 | "react-dom": "^17.0.2",
10 | "react-redux": "^7.2.4",
11 | "react-router-dom": "^5.2.0",
12 | "redux": "^4.1.0",
13 | "redux-devtools-extension": "^2.13.9",
14 | "redux-logger": "^3.0.6",
15 | "redux-thunk": "^2.3.0"
16 | },
17 | "devDependencies": {
18 | "@mfe/feature-01": "link: ../frontend-components/feature-01",
19 | "@mfe/shared": "link: ../shared-components/",
20 | "@testing-library/jest-dom": "^5.12.0",
21 | "@testing-library/react": "^11.2.6",
22 | "@testing-library/user-event": "^12.8.3",
23 | "@types/jest": "^26.0.23",
24 | "@types/node": "^12.20.11",
25 | "@types/node-sass": "^4.11.1",
26 | "@types/prettier": "^2.2.3",
27 | "@types/react": "^17.0.4",
28 | "@types/react-dom": "^17.0.3",
29 | "@types/react-redux": "^7.1.16",
30 | "@types/react-router-dom": "^5.1.7",
31 | "@types/redux-logger": "^3.0.8",
32 | "@types/testing-library__jest-dom": "^5.9.5",
33 | "cross-env": "^7.0.3",
34 | "cypress": "^8.3.1",
35 | "husky": "^6.0.0",
36 | "lint-staged": "^10.5.4",
37 | "node-sass": "^4.0.0",
38 | "prettier": "^2.2.1",
39 | "react-scripts": "4.0.1",
40 | "stylis-plugin-extra-scope": "^0.3.0",
41 | "typescript": "^4.0.3",
42 | "typesync": "^0.8.0",
43 | "web-vitals": "^1.1.1",
44 | "workbox-background-sync": "^5.1.3",
45 | "workbox-broadcast-update": "^5.1.3",
46 | "workbox-cacheable-response": "^5.1.3",
47 | "workbox-core": "^5.1.3",
48 | "workbox-expiration": "^5.1.3",
49 | "workbox-google-analytics": "^5.1.3",
50 | "workbox-navigation-preload": "^5.1.3",
51 | "workbox-precaching": "^5.1.3",
52 | "workbox-range-requests": "^5.1.3",
53 | "workbox-routing": "^5.1.3",
54 | "workbox-strategies": "^5.1.3",
55 | "workbox-streams": "^5.1.3"
56 | },
57 | "resolutions": {
58 | "**/react": "^17.0.2",
59 | "**/react-dom": "^17.0.2"
60 | },
61 | "scripts": {
62 | "postinstall": "typesync",
63 | "start": "react-scripts start",
64 | "build": "react-scripts build",
65 | "test": "react-scripts test",
66 | "eject": "react-scripts eject"
67 | },
68 | "eslintConfig": {
69 | "extends": [
70 | "react-app",
71 | "react-app/jest"
72 | ]
73 | },
74 | "browserslist": {
75 | "production": [
76 | ">0.2%",
77 | "not dead",
78 | "not op_mini all"
79 | ],
80 | "development": [
81 | "last 1 chrome version",
82 | "last 1 firefox version",
83 | "last 1 safari version"
84 | ]
85 | },
86 | "prettier": {
87 | "singleQuote": true
88 | },
89 | "lint-staged": {
90 | "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
91 | "prettier --write",
92 | "git add"
93 | ]
94 | },
95 | "husky": {
96 | "hooks": {
97 | "pre-commit": "lint-staged"
98 | }
99 | },
100 | "dependencies": {
101 | "react-loader-spinner": "^4.0.0"
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/packages/core-ui/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/favicon.ico
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-128x128.png
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-144x144.png
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-152x152.png
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-192x192.png
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-384x384.png
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-512x512.png
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-72x72.png
--------------------------------------------------------------------------------
/packages/core-ui/public/icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/public/icon-96x96.png
--------------------------------------------------------------------------------
/packages/core-ui/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
16 |
17 |
26 | Food HQ
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/packages/core-ui/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Food HQ",
3 | "name": "Food HQ",
4 | "theme_color": "#e38b06",
5 | "background_color": "#ffffff",
6 | "display": "standalone",
7 | "orientation": "portrait",
8 | "Scope": "/",
9 | "start_url": "/",
10 | "icons": [
11 | {
12 | "src": "icon-72x72.png",
13 | "sizes": "72x72",
14 | "type": "image/png"
15 | },
16 | {
17 | "src": "icon-96x96.png",
18 | "sizes": "96x96",
19 | "type": "image/png"
20 | },
21 | {
22 | "src": "icon-128x128.png",
23 | "sizes": "128x128",
24 | "type": "image/png"
25 | },
26 | {
27 | "src": "icon-144x144.png",
28 | "sizes": "144x144",
29 | "type": "image/png"
30 | },
31 | {
32 | "src": "icon-152x152.png",
33 | "sizes": "152x152",
34 | "type": "image/png"
35 | },
36 | {
37 | "src": "icon-192x192.png",
38 | "sizes": "192x192",
39 | "type": "image/png"
40 | },
41 | {
42 | "src": "icon-384x384.png",
43 | "sizes": "384x384",
44 | "type": "image/png"
45 | },
46 | {
47 | "src": "icon-512x512.png",
48 | "sizes": "512x512",
49 | "type": "image/png"
50 | }
51 | ],
52 | "splash_pages": null
53 | }
--------------------------------------------------------------------------------
/packages/core-ui/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/packages/core-ui/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/packages/core-ui/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from 'react';
2 | import { Router } from 'react-router-dom';
3 | import { history } from './config/routes';
4 | import { Routes } from './Routes';
5 | import { Loader } from './components/Loader';
6 | import { Footer } from './components/Footer';
7 | import { Hero } from './components/Hero';
8 | import ErrorBoundary from './components/ErrorBoundary';
9 |
10 | export const App: React.FC = () => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | );
22 | };
23 |
24 | export default App;
25 |
--------------------------------------------------------------------------------
/packages/core-ui/src/GlobalStyles.ts:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components';
2 | import { OutlineButton, Container } from '@mfe/shared/dist/bundle';
3 |
4 | export type IThemeColors = {
5 | hover: string,
6 | background: string,
7 | };
8 |
9 | declare module 'styled-components' {
10 | export interface DefaultTheme {
11 | body: string;
12 | text: string;
13 | colors: IThemeColors;
14 | }
15 | }
16 |
17 | const GlobalStyles = createGlobalStyle`
18 | @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;800;900&family=Rubik:wght@800&display=swap');
19 | *{
20 | margin: 0;
21 | padding: 0;
22 | box-sizing: inherit;
23 | }
24 | html {
25 | box-sizing: border-box;
26 | font-size: 62.5%;
27 | @media only screen and (max-width: 1200px){
28 | font-size: 58%;
29 | }
30 | @media only screen and (min-width: 1980px){
31 | font-size: 70%;
32 | }
33 | }
34 | body{
35 | font-family: 'Nunito', sans-serif;
36 | font-weight: 400;
37 | line-height: 1.6;
38 | font-size: 1.6rem;
39 | background: ${({ theme }) => theme.body};
40 | }`;
41 |
42 | export default GlobalStyles;
43 |
44 | export const lightTheme = {
45 | body: '#F9F9F9',
46 | text: '#fff',
47 | colors : {
48 | hover: '#e38b06',
49 | background: '#333'
50 | }
51 | };
52 |
53 | export const darkTheme = {
54 | body: '#cccc',
55 | text: '#e38b06',
56 | colors : {
57 | hover: '#fff',
58 | background: '#333'
59 | }
60 | };
61 |
62 | export type IStyledOutlineButtonProps = {
63 | bigRadius?: boolean;
64 | big?: boolean;
65 | fontBig?: boolean;
66 | bigFont?: boolean;
67 | };
68 |
69 | export { OutlineButton, Container };
70 |
--------------------------------------------------------------------------------
/packages/core-ui/src/Routes/Routes.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, Switch } from 'react-router-dom';
3 | import { routes } from '../config/routes';
4 |
5 | export const Routes: React.FC = () => {
6 | return (
7 |
8 | {Object.keys(routes).map((key) => {
9 | const { component: Component, exact, path, id } = routes[key];
10 | return (
11 |
12 | );
13 | })}
14 |
15 | );
16 | };
17 |
18 | export default Routes;
19 |
--------------------------------------------------------------------------------
/packages/core-ui/src/Routes/index.tsx:
--------------------------------------------------------------------------------
1 | import Routes from './Routes';
2 |
3 | export { Routes };
--------------------------------------------------------------------------------
/packages/core-ui/src/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ThemeProvider } from 'styled-components';
3 | import { useSelector, shallowEqual } from 'react-redux';
4 | import { lightTheme, darkTheme } from './GlobalStyles';
5 |
6 | interface IProps {
7 | children: any;
8 | }
9 |
10 | export const Theme: React.FC = (props) => {
11 | const { children } = props;
12 | const themeMode: string = useSelector(
13 | (state: any) => state.app.themeMode,
14 | shallowEqual
15 | );
16 |
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | };
23 |
24 | export default Theme;
25 |
--------------------------------------------------------------------------------
/packages/core-ui/src/actions-types/index.ts:
--------------------------------------------------------------------------------
1 | export const INCRIMENT_COUNT : String = 'INCRIMENT_COUNT';
2 | export const DECRIMENT_COUNT : String = 'DECRIMENT_COUNT';
3 |
4 | export const SET_APP_THEME : String = 'SET_APP_THEME';
5 |
--------------------------------------------------------------------------------
/packages/core-ui/src/actions/app.ts:
--------------------------------------------------------------------------------
1 | import * as types from '../actions-types';
2 |
3 | export const setTheme = (payload: string) => ({
4 | type: types.SET_APP_THEME,
5 | payload,
6 | });
7 |
--------------------------------------------------------------------------------
/packages/core-ui/src/actions/counter.ts:
--------------------------------------------------------------------------------
1 | import * as types from '../actions-types';
2 |
3 | export const incriment = () => ({
4 | type: types.INCRIMENT_COUNT,
5 | });
6 |
7 | export const decrement = () => ({
8 | type: types.DECRIMENT_COUNT,
9 | });
10 |
--------------------------------------------------------------------------------
/packages/core-ui/src/assets/images/hero-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/src/assets/images/hero-bg.png
--------------------------------------------------------------------------------
/packages/core-ui/src/assets/images/pizza.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/src/assets/images/pizza.png
--------------------------------------------------------------------------------
/packages/core-ui/src/assets/images/salmon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/core-ui/src/assets/images/salmon.png
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Counter.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dispatch } from 'redux';
3 | import { useSelector, shallowEqual, useDispatch } from 'react-redux';
4 | import { incriment, decrement } from '../actions/counter';
5 | import { Button } from '@mfe/shared/dist/bundle';
6 |
7 | export const Counter: React.FC = () => {
8 | const dispatch: Dispatch = useDispatch();
9 | const count: number = useSelector(
10 | (state: any) => state.counter.count,
11 | shallowEqual
12 | );
13 |
14 | const onIncriment = () => {
15 | dispatch(incriment());
16 | };
17 |
18 | const onDecrement = () => {
19 | dispatch(decrement());
20 | };
21 |
22 | return (
23 | <>
24 |
25 | count is :{count}
26 |
27 | >
28 | );
29 | };
30 |
31 | export default Counter;
32 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | interface IState {
4 | error: any;
5 | errorInfo: any;
6 | }
7 |
8 | class ErrorBoundary extends React.Component<{}, IState> {
9 | constructor(props) {
10 | super(props);
11 | this.state = { error: null, errorInfo: null };
12 | }
13 |
14 | componentDidCatch(error, errorInfo) {
15 | // Catch errors in any components below and re-render with error message
16 | this.setState({
17 | error,
18 | errorInfo
19 | })
20 | // You can also log error messages to an error reporting service here
21 | }
22 |
23 | render() {
24 | if (this.state.errorInfo) {
25 | // Error path
26 | return (
27 |
28 |
Something went wrong.
29 |
30 | {this.state.error && this.state.error.toString()}
31 |
32 | {this.state.errorInfo.componentStack}
33 |
34 |
35 | );
36 | }
37 | // Normally, just render children
38 | return this.props.children;
39 | }
40 | }
41 |
42 | export default ErrorBoundary;
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Footer/Footer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | FooterSection,
4 | FooterContainer,
5 | FooterCopyRight,
6 | } from './Styled';
7 |
8 | export const Footer: React.FC = () => {
9 | const year = new Date().getFullYear();
10 |
11 | return (
12 |
13 |
14 |
15 | © Copyright {year}
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default Footer;
23 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Footer/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const FooterSection = styled.section`
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | color: ${({ theme }) => theme.text};
8 | background-color: ${({ theme }) => theme.colors.background};
9 | padding: 3rem;
10 | `;
11 |
12 | export const FooterContainer = styled.div`
13 | display: flex;
14 | `;
15 |
16 | export const FooterCopyRight = styled.a`
17 | text-decoration: none;
18 | outline: none;
19 | color: ${({ theme }) => theme.text};
20 | &:hover {
21 | color: ${({ theme }) => theme.colors.hover};
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Footer/index.tsx:
--------------------------------------------------------------------------------
1 | import { Footer } from './Footer';
2 |
3 | export { Footer };
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import { Navbar } from './Navbar';
2 |
3 | const Header: React.FC = () => {
4 | return (
5 | <>
6 |
7 | >
8 | );
9 | };
10 |
11 | export default Header;
12 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Hero/Hero.tsx:
--------------------------------------------------------------------------------
1 | import Navbar from '../Navbar/Navbar';
2 | import {
3 | HeroContainer,
4 | HeroContent,
5 | HeroContentText,
6 | HeroTitle,
7 | HeroTitleText,
8 | HeroSubTitle,
9 | HeroText,
10 | } from './Styled';
11 |
12 | export const Hero: React.FC = () => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 | Healthy
20 | Meals All Day
21 |
22 | For a longer Life
23 |
24 | Get a fresh and tasty recepies that are well balanced and will
25 | improve your wellness, plus add nutrients to your body.
26 |
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | export default Hero;
34 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Hero/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { images } from '../../config/images'
3 |
4 | export const HeroContainer = styled.div`
5 | background-image: linear-gradient(
6 | to top right,
7 | rgba(11, 10, 10, 0.38),
8 | rgba(11, 10, 10, 0.19)
9 | ),
10 | url(${images.HeroBg.default});
11 | background-position: center;
12 | background-repeat: no-repeat;
13 | background-size: cover;
14 | height: 54vh;
15 | @media only screen and (max-width: 1600px) {
16 | height: 65vh;
17 | }
18 | `;
19 |
20 | export const HeroContent = styled.section`
21 | height: 100%;
22 | width: 100%;
23 | /* position: relative; */
24 | display: flex;
25 | flex-direction: column;
26 | justify-content: center;
27 | align-items: center;
28 | text-align: center;
29 | color: #fffefe;
30 | @media only screen and (max-width: 375px) {
31 | text-align: start;
32 | height: 80%;
33 | }
34 | `;
35 |
36 | export const HeroContentText = styled.div`
37 | width: 50%;
38 | display: flex;
39 | flex-direction: column;
40 | justify-content: center;
41 | align-items: center;
42 | @media only screen and (max-width: 600px) {
43 | width: 80%;
44 | }
45 | @media only screen and (max-width: 375px) {
46 | position: absolute;
47 | align-items: flex-start;
48 | }
49 | `;
50 |
51 | export const HeroTitle = styled.h1`
52 | font-size: clamp(1rem, 10vw, 5rem);
53 | font-weight: 900;
54 | letter-spacing: 0.5rem;
55 | line-height: 1.3;
56 | `;
57 |
58 | export const HeroTitleText = styled.span`
59 | display: block;
60 | `;
61 |
62 | export const HeroSubTitle = styled.h2`
63 | font-size: clamp(2rem, 3vw, 4rem);
64 | font-weight: 300;
65 | letter-spacing: 1rem;
66 | padding-top: 1rem;
67 | `;
68 |
69 | export const HeroText = styled.h3`
70 | font-size: clamp(2rem, 2.5vw, 3rem);
71 | font-weight: 400;
72 | padding: 2.5rem 2rem;
73 | @media only screen and (max-width: 375px) {
74 | padding: 1.5rem 0;
75 | }
76 | `;
77 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Hero/index.tsx:
--------------------------------------------------------------------------------
1 | import { Hero } from './Hero';
2 |
3 | export { Hero };
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Loader/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const LoaderContainer = styled.div`
4 | display: flex;
5 | width: 100%;
6 | height: 100vh;
7 | align-items: center;
8 | justify-content: center;
9 | &.hide {
10 | display: none;
11 | }
12 | `;
13 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Loader/index.tsx:
--------------------------------------------------------------------------------
1 | import Spinner from 'react-loader-spinner';
2 | import { LoaderContainer } from './Styled';
3 |
4 | export const Loader: React.FC = () => {
5 | return (
6 |
7 |
8 |
9 | );
10 | };
11 |
12 | export default Loader;
13 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Navbar/Navbar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { IconContext } from 'react-icons';
3 | import { BiMenu, BiX } from 'react-icons/bi';
4 | import { routes } from '../../config/routes_config';
5 | import { ToggleSwitch } from '../ToggleSwitch';
6 |
7 | import {
8 | Nav,
9 | NavbarContainer,
10 | NavLogo,
11 | NavIcon,
12 | MenuIcon,
13 | Menu,
14 | MenuItem,
15 | MenuLink,
16 | } from './Styled';
17 |
18 | export const Navbar: React.FC = () => {
19 | const [click, setClick] = useState(false);
20 |
21 | const handleClick = () => setClick(!click);
22 | const closeMenu = () => setClick(false);
23 |
24 | return (
25 |
26 |
27 |
56 |
57 |
58 | );
59 | };
60 |
61 | export default Navbar;
62 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Navbar/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { Link } from 'react-router-dom';
3 | import { BiRestaurant } from 'react-icons/bi';
4 | import { Container } from '../../GlobalStyles';
5 |
6 | export const Nav = styled.nav`
7 | font-size: 18px;
8 | position: sticky;
9 | top: 0;
10 | z-index: 999;
11 | height: 80px;
12 | background-color: rgba(0, 0, 0, 0.5);
13 | /* box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.5); */
14 | box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.15);
15 | display: flex;
16 | justify-content: center;
17 | align-items: center;
18 | `;
19 |
20 | export const NavbarContainer = styled(Container)`
21 | display: flex;
22 | justify-content: space-between;
23 | align-items: center;
24 | height: 80px;
25 | ${Container};
26 | `;
27 |
28 | export const NavLogo = styled(Link)`
29 | color: #fff;
30 | cursor: pointer;
31 | display: flex;
32 | align-items: center;
33 | text-decoration: none;
34 | font-size: 2.5rem;
35 | font-weight: 800;
36 | transition: all 0.5s ease;
37 | &:hover {
38 | transform: scale(1.08);
39 | }
40 | `;
41 |
42 | export const NavIcon = styled(BiRestaurant)`
43 | margin-right: 0.8rem;
44 | transition: all 0.5s ease;
45 | &:hover {
46 | transform: scale(2);
47 | }
48 | `;
49 |
50 | export const MenuIcon = styled.div`
51 | display: none;
52 | @media (max-width: 1000px) {
53 | display: block;
54 | position: absolute;
55 | top: 0;
56 | right: 0;
57 | transform: translate(-50%, 20%);
58 | font-size: 4rem;
59 | cursor: pointer;
60 | }
61 | `;
62 |
63 | export const Menu = styled.ul`
64 | display: flex;
65 | align-items: center;
66 | text-align: center;
67 | @media only screen and (max-width: 1000px) {
68 | display: flex;
69 | flex-direction: column;
70 | width: 100%;
71 | height: 100vh;
72 | position: absolute;
73 | top: 80px;
74 | left: ${({ click }: { click: any }) => (click ? '0' : '-100%')};
75 | background-color: rgba(0, 0, 0, 0.9);
76 | transition: all 0.5s ease;
77 | }
78 | `;
79 |
80 | export const MenuItem = styled.li`
81 | list-style: none;
82 | height: 80px;
83 | @media only screen and (max-width: 1000px) {
84 | width: 100%;
85 | &:hover {
86 | border: none;
87 | }
88 | }
89 | `;
90 |
91 | export const MenuLink = styled(Link)`
92 | text-decoration: none;
93 | font-weight: bold;
94 | font-size: 2rem;
95 | color: ${({ theme }) => theme.text};
96 | display: flex;
97 | justify-content: space-between;
98 | align-items: center;
99 | padding: 1rem 2rem;
100 | height: 100%;
101 | transition: all 0.2s ease;
102 | &:hover {
103 | color: ${({ theme }) => theme.colors.hover};
104 | transform: traslateY(-3rem);
105 | }
106 | &:active {
107 | transform: traslateY(3rem);
108 | color: ${({ theme }) => theme.colors.hover};
109 | }
110 | @media only screen and (max-width: 1000px) {
111 | display: block;
112 | padding: 3rem;
113 | text-align: center;
114 | transition: all 0.2s ease;
115 | }
116 | `;
117 |
118 | export const MenuItemBtn = styled.li`
119 | list-style: none;
120 | @media screen and (max-width: 1000px) {
121 | display: flex;
122 | justify-content: center;
123 | align-items: center;
124 | width: 50%;
125 | height: 120px;
126 | }
127 | `;
128 |
129 | export const MenuLinkBtn = styled(Link)`
130 | text-decoration: none;
131 | display: flex;
132 | justify-content: center;
133 | align-items: center;
134 | padding: 8px 16px;
135 | height: 100%;
136 | width: 100%;
137 | border: none;
138 | outline: none;
139 | `;
140 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Navbar/index.tsx:
--------------------------------------------------------------------------------
1 | import { Navbar } from './Navbar';
2 |
3 | export { Navbar };
--------------------------------------------------------------------------------
/packages/core-ui/src/components/NotFound/NotFound.tsx:
--------------------------------------------------------------------------------
1 | import { NotFoundWrapper, NotFoundContainer, NotFoundTitle } from './Styled';
2 |
3 | export const NotFound: React.FC = () => {
4 | return (
5 |
6 |
7 | 404 Page not found
8 |
9 |
10 | );
11 | };
12 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/NotFound/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { Container } from '../../GlobalStyles';
3 |
4 | export const NotFoundWrapper = styled.section`
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | align-items: center;
9 | margin: 16rem 0;
10 | `;
11 |
12 | export const NotFoundContainer = styled(Container)`
13 | display: flex;
14 | flex-direction: column;
15 | ${Container};
16 | `;
17 |
18 | export const NotFoundTitle = styled.h2`
19 | font-size: clamp(2rem, 8vw, 5rem);
20 | text-align: center;
21 | margin-bottom: 3rem;
22 | font-weight: bold;
23 | @media only screen and (max-width: 700px) {
24 | margin-bottom: 0;
25 | }
26 | `;
27 |
28 | export const Img = styled.img`
29 | height: 50rem;
30 | `;
--------------------------------------------------------------------------------
/packages/core-ui/src/components/NotFound/index.tsx:
--------------------------------------------------------------------------------
1 | import { NotFound } from './NotFound';
2 |
3 | export { NotFound };
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Recipes/Recipes.tsx:
--------------------------------------------------------------------------------
1 | import { OutlineButton } from '../../GlobalStyles';
2 | import { images } from '../../config/images';
3 | import { routes } from '../../config/routes_config';
4 | import { history } from '../../config/routes';
5 | import {
6 | RecipeContainer,
7 | RecipeWrapper,
8 | RecipeTitle,
9 | RecipeContentContainer,
10 | RecipeTabContainer,
11 | RecipeBtn,
12 | RecipeCardWrapper,
13 | RecipeFeature,
14 | RecipeFeatureContent,
15 | RecipeFeatureTitle,
16 | RecipeFeatureText,
17 | RecipeFeatureDetails,
18 | RecipeFeatureItem,
19 | RecipeItemTitle,
20 | RecipeItemContent,
21 | RecipeItemIcon,
22 | RecipeItemText,
23 | RecipeCardSection,
24 | RecipeSmallCards,
25 | RecipeCard,
26 | RecipeCardContent,
27 | RecipeCardHeading,
28 | RecipeCardDetails,
29 | RecipeCardItems,
30 | RecipeCardTitle,
31 | RecipeCardItem,
32 | RecipeCardIcon,
33 | RecipeCardText,
34 | RecipeImg,
35 | Img,
36 | } from './Styled';
37 |
38 | export const Recipes: React.FC = () => {
39 | const onDetailsPage = (type: string) => {
40 | const path = `${routes.featureOneApp.path_string()}/recipe-details/${type}`;
41 | history.push(path);
42 | };
43 |
44 | return (
45 |
46 |
47 | Recipes
48 |
49 |
50 |
51 |
52 | Sea Food
53 |
54 |
55 |
56 |
57 | Vegetarian
58 |
59 |
60 |
61 |
62 | Meat and Poultry
63 |
64 |
65 |
66 |
67 | Easy meal preps
68 |
69 |
70 |
71 |
72 | onDetailsPage('Chicken Recipe')}>
73 |
74 |
75 |
76 | Salmon and Hot Honey Butter
77 |
78 |
79 | The hot honey kinda sounds like a hype name used before the
80 | 20s, (I used it back then). It’s a pefect coating for the
81 | salmon to enrinch it with sweetness and heat.
82 |
83 |
84 |
85 | Serving
86 |
87 |
88 | 2
89 |
90 |
91 |
92 | Cook time
93 | 35-45 min
94 |
95 |
96 | Difficulty level
97 | 20%
98 |
99 |
100 |
101 |
102 |
103 |
104 | onDetailsPage('Chicken Recipe')}>
105 |
106 |
107 |
108 | Pretzel-Crusted Chicken
109 |
110 |
111 |
112 | Serving
113 |
114 |
115 | 2
116 |
117 |
118 |
119 | Cook time
120 | 30-45 min
121 |
122 |
123 | Difficulty level
124 | 20%
125 |
126 |
127 |
128 |
129 | onDetailsPage('Chicken Recipe')}>
130 |
131 |
132 | Sesame Asian Salad
133 |
134 |
135 | Serving
136 |
137 |
138 | 2
139 |
140 |
141 |
142 | Cook time
143 | 10-15 min
144 |
145 |
146 | Difficulty level
147 | 10%
148 |
149 |
150 |
151 |
152 |
153 |
154 | onDetailsPage('Chicken Recipe')}>
155 |
156 |
157 |
158 | Italian Pizza with Cheese
159 |
160 |
161 |
162 | Serving
163 |
164 |
165 | 2
166 |
167 |
168 |
169 | Cook time
170 | 30-45 min
171 |
172 |
173 | Difficulty level
174 | 20%
175 |
176 |
177 |
178 |
179 | onDetailsPage('Chicken Recipe')}>
180 |
181 |
182 |
183 | Pasta with creamy sause
184 |
185 |
186 |
187 | Serving
188 |
189 |
190 | 2
191 |
192 |
193 |
194 | Cook time
195 | 10-15 min
196 |
197 |
198 | Difficulty level
199 | 10%
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | );
211 | };
212 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Recipes/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { HiUsers } from 'react-icons/hi';
3 | import { Container } from '../../GlobalStyles';
4 |
5 | export const RecipeWrapper = styled.section`
6 | display: flex;
7 | flex-direction: column;
8 | justify-content: center;
9 | align-items: center;
10 | margin: 6rem 0;
11 | `;
12 |
13 | export const RecipeContainer = styled(Container)`
14 | display: flex;
15 | flex-direction: column;
16 | ${Container};
17 | `;
18 |
19 | export const RecipeTitle = styled.h2`
20 | font-size: clamp(2rem, 8vw, 5rem);
21 | text-align: center;
22 | margin-bottom: 3rem;
23 | font-weight: bold;
24 | @media only screen and (max-width: 700px) {
25 | margin-bottom: 0;
26 | }
27 | `;
28 |
29 | export const RecipeContentContainer = styled.div`
30 | display: flex;
31 | flex-direction: column;
32 | justify-content: center;
33 | align-items: center;
34 | `;
35 |
36 | export const RecipeTabContainer = styled.div`
37 | display: flex;
38 | justify-content: center;
39 | align-items: center;
40 | @media only screen and (max-width: 700px) {
41 | display: none;
42 | }
43 | `;
44 |
45 | export const RecipeBtn = styled.div`
46 | &:not(:last-child) {
47 | margin-right: 3rem;
48 | }
49 | @media only screen and (max-width: 700px) {
50 | display: none;
51 | }
52 | `;
53 |
54 | //Recipe card section
55 |
56 | export const RecipeCardWrapper = styled.div`
57 | display: flex;
58 | justify-content: space-between;
59 | align-items: center;
60 | margin-top: 6rem;
61 | flex-direction: column;
62 | @media only screen and (min-width: 1800px) {
63 | flex-direction: row;
64 | }
65 | `;
66 |
67 | export const RecipeFeature = styled.div`
68 | display: flex;
69 | justify-content: center;
70 | align-items: center;
71 | width: 650px;
72 | height: 320px;
73 | background-color: #fff;
74 | box-shadow: 0px 8px 30px rgba(0, 0, 0, 0.18);
75 | border-radius: 40px;
76 | transition: all 0.4s ease;
77 | margin-bottom: 5rem;
78 | cursor: pointer;
79 | @media only screen and (min-width: 1800px) {
80 | margin-right: 10rem;
81 | padding: 0;
82 | }
83 | &:hover {
84 | box-shadow: 0px 10px 80px rgba(0, 0, 0, 0.12);
85 | transform: scale(1.05);
86 | background-color: #333;
87 | color: #fff;
88 | }
89 | @media only screen and (min-width: 1000px) {
90 | flex-direction: row;
91 | }
92 | @media only screen and (max-width: 900px) {
93 | margin-bottom: 10rem;
94 | }
95 | @media only screen and (max-width: 700px) {
96 | width: 550px;
97 | margin-bottom: 3rem;
98 | &:hover {
99 | transform: scale(1.02);
100 | }
101 | }
102 | @media only screen and (max-width: 600px) {
103 | width: 500px;
104 | }
105 | @media only screen and (max-width: 500px) {
106 | width: 380px;
107 | height: 300px;
108 | flex-direction: column;
109 | justify-content: flex-start;
110 | box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.1);
111 | }
112 | @media only screen and (max-width: 400px) {
113 | width: 330px;
114 | }
115 | `;
116 |
117 | export const RecipeImg = styled.img`
118 | height: 80%;
119 | margin-top: 2rem;
120 | margin-left: 2rem;
121 | transition: all 0.5s ease;
122 | &:hover {
123 | transform: scale(1.14) rotate(360deg);
124 | }
125 | @media only screen and (max-width: 700px) {
126 | height: 58%;
127 | }
128 | @media only screen and (max-width: 600px) {
129 | height: 50%;
130 | }
131 | @media only screen and (max-width: 500px) {
132 | height: 30%;
133 | }
134 | `;
135 |
136 | export const RecipeFeatureContent = styled.div`
137 | display: flex;
138 | flex-direction: column;
139 | justify-content: center;
140 | padding: 2rem;
141 | @media only screen and (max-width: 500px) {
142 | padding: 2rem;
143 | align-items: center;
144 | }
145 | `;
146 |
147 | export const RecipeFeatureTitle = styled.h3`
148 | font-size: 3.6rem;
149 | font-weight: 400;
150 | line-height: 1.3;
151 | margin-bottom: 0.6rem;
152 | @media only screen and (max-width: 700px) {
153 | font-size: 3rem;
154 | }
155 | @media only screen and (max-width: 600px) {
156 | font-size: 2.5rem;
157 | }
158 | @media only screen and (max-width: 400px) {
159 | font-size: 2rem;
160 | }
161 | `;
162 |
163 | export const RecipeFeatureText = styled.p`
164 | font-size: 1.6rem;
165 | padding-bottom: 1.3rem;
166 | @media only screen and (max-width: 700px) {
167 | font-size: 1.4rem;
168 | }
169 | @media only screen and (max-width: 400px) {
170 | font-size: 1.3rem;
171 | }
172 | `;
173 |
174 | export const RecipeFeatureDetails = styled.div`
175 | display: flex;
176 | align-items: center;
177 | `;
178 |
179 | export const RecipeFeatureItem = styled.div`
180 | display: flex;
181 | align-items: flex-start;
182 | flex-direction: column;
183 | margin-right: 2.5rem;
184 | `;
185 |
186 | export const RecipeItemTitle = styled.h4`
187 | font-size: 1.4rem;
188 | @media only screen and (max-width: 400px) {
189 | font-size: 1.3rem;
190 | }
191 | `;
192 |
193 | export const RecipeItemContent = styled.div`
194 | display: flex;
195 | align-items: center;
196 | `;
197 |
198 | export const RecipeItemIcon = styled(HiUsers)`
199 | color: #e38b06;
200 | font-size: 3rem;
201 | padding-right: 1rem;
202 | `;
203 |
204 | export const RecipeItemText = styled.p`
205 | font-size: 1.8rem;
206 | @media only screen and (max-width: 700px) {
207 | font-size: 1.6rem;
208 | }
209 | `;
210 |
211 | export const RecipeCardSection = styled.div`
212 | display: flex;
213 | flex-direction: column;
214 | justify-content: center;
215 | align-items: center;
216 | @media only screen and (min-width: 900px) {
217 | flex-direction: row;
218 | }
219 | `;
220 |
221 | export const RecipeSmallCards = styled.div`
222 | display: flex;
223 | flex-direction: column;
224 | align-items: center;
225 | justify-content: center;
226 | @media only screen and (min-width: 1200px) {
227 | margin-left: 2rem;
228 | }
229 | @media only screen and (min-width: 1500px) {
230 | margin-left: 3.5rem;
231 | }
232 | `;
233 |
234 | export const RecipeCard = styled.div`
235 | display: flex;
236 | justify-content: center;
237 | align-items: center;
238 | width: 500px;
239 | height: 130px;
240 | background: #ffffff;
241 | box-shadow: 0px 10px 30px rgba(0, 0, 0, 0.15);
242 | border-radius: 20px;
243 | transition: all 0.5s ease;
244 | margin-bottom: 4rem;
245 | cursor: pointer;
246 | &:hover {
247 | background-color: #333;
248 | color: #fff;
249 | transform: scale(1.05);
250 | box-shadow: 0px 10px 50px rgba(0, 0, 0, 0.2);
251 | }
252 | @media only screen and (max-width: 1200px) {
253 | width: 397px;
254 | margin-right: 5rem;
255 | }
256 | @media only screen and (max-width: 1000px) {
257 | width: 420px;
258 | margin-right: 2.5rem;
259 | }
260 |
261 | @media only screen and (max-width: 700px) {
262 | width: 380px;
263 | &:hover {
264 | transform: scale(1.1);
265 | }
266 | }
267 | @media only screen and (max-width: 500px) {
268 | margin-right: 0;
269 | &:hover {
270 | transform: scale(1);
271 | }
272 | }
273 | @media only screen and (max-width: 400px) {
274 | flex-direction: column;
275 | width: 280px;
276 | }
277 | `;
278 |
279 | export const RecipeCardImg = styled.div`
280 | height: 10.3rem;
281 | margin-left: -10rem;
282 | `;
283 |
284 | export const RecipeCardContent = styled.div`
285 | display: flex;
286 | flex-direction: column;
287 | align-items: flex-start;
288 | padding: 0 2rem;
289 | `;
290 |
291 | export const RecipeCardHeading = styled.h3`
292 | font-size: 2.4rem;
293 | font-weight: 400;
294 | @media only screen and (max-width: 700px) {
295 | font-size: 2rem;
296 | }
297 | `;
298 |
299 | export const RecipeCardDetails = styled.div`
300 | display: flex;
301 | align-items: center;
302 | margin-top: 1.5rem;
303 | `;
304 |
305 | export const RecipeCardItems = styled.div`
306 | display: flex;
307 | flex-direction: column;
308 | align-items: flex-start;
309 | text-align: center;
310 | &:not(:last-child) {
311 | margin-right: 2.5rem;
312 | }
313 | `;
314 |
315 | export const RecipeCardTitle = styled.h4`
316 | font-size: 1.4rem;
317 | @media only screen and (max-width: 700px) {
318 | font-size: 1.2rem;
319 | }
320 | `;
321 |
322 | export const RecipeCardItem = styled.div`
323 | display: flex;
324 | justify-content: center;
325 | align-items: center;
326 | margin-right: 2.5rem;
327 | `;
328 |
329 | export const RecipeCardIcon = styled(HiUsers)`
330 | color: #e38b06;
331 | font-size: 2rem;
332 | margin-right: 1.3rem;
333 | @media only screen and (max-width: 700px) {
334 | font-size: 1.5rem;
335 | margin-right: 1rem;
336 | }
337 | `;
338 |
339 | export const RecipeCardText = styled.p`
340 | font-size: 1.4rem;
341 | @media only screen and (max-width: 700px) {
342 | font-size: 1.2rem;
343 | }
344 | `;
345 |
346 | export const RecipeFeatureImg = styled.div`
347 | width: 100%;
348 | display: flex;
349 | justify-content: center;
350 | align-items: center;
351 | height: 100%;
352 | `;
353 |
354 | export const Img = styled.img`
355 | height: 10rem;
356 | transition: all 0.5s ease;
357 | &:hover {
358 | transform: scale(1.06) rotate(360deg);
359 | }
360 | @media only screen and (max-width: 1000px) {
361 | height: 9rem;
362 | }
363 | @media only screen and (max-width: 400px) {
364 | display: none;
365 | }
366 | `;
367 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Recipes/index.tsx:
--------------------------------------------------------------------------------
1 | import { Recipes } from './Recipes';
2 |
3 | export { Recipes };
--------------------------------------------------------------------------------
/packages/core-ui/src/components/ToggleSwitch/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const CheckBoxWrapper = styled.div`
4 | position: relative;
5 |
6 | `;
7 |
8 | export const CheckBoxLabel = styled.label`
9 | position: absolute;
10 | top: 0;
11 | left: 0;
12 | right: 0;
13 | bottom: 0;
14 | width: 50px;
15 | height: 26px;
16 | border-radius: 15px;
17 | background: #bebebe;
18 | cursor: pointer;
19 | &::after {
20 | content: '';
21 | display: block;
22 | border-radius: 50%;
23 | width: 26px;
24 | height: 26px;
25 | margin: 0px 5px 5px 0px;
26 | background: #ffffff;
27 | box-shadow: 1px 3px 3px 1px rgba(0, 0, 0, 0.2);
28 | -webkit-transition: 0.4s;
29 | transition: 0.4s;
30 | }
31 | `;
32 |
33 | export const CheckBox = styled.input`
34 | opacity: 0;
35 | z-index: 1;
36 | border-radius: 15px;
37 | width: 50px;
38 | &:checked + ${CheckBoxLabel} {
39 | background: #2196f3;
40 | &::after {
41 | content: '';
42 | display: block;
43 | border-radius: 50%;
44 | width: 26px;
45 | height: 26px;
46 | margin: 0px 5px 5px 0px;
47 | margin-left: 23px;
48 | -webkit-transition: 0.4s;
49 | transition: 0.4s;
50 | }
51 | }
52 | `;
53 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/ToggleSwitch/ToggleSwitch.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { useDispatch } from 'react-redux';
3 | import { CheckBoxWrapper, CheckBox, CheckBoxLabel } from './Styled';
4 | import { setTheme } from '../../actions/app';
5 |
6 | export const ToggleSwitch = () => {
7 | const dispatch = useDispatch();
8 | const [isDarkTheme, setDarkTheme] = useState(false);
9 |
10 | useEffect(() => {
11 | const isDarkTheme = localStorage.getItem('theme') === 'dark';
12 | setDarkTheme(isDarkTheme);
13 | }, []);
14 |
15 | const toggleTheme = () => {
16 | setDarkTheme(!isDarkTheme);
17 | const themeMode = isDarkTheme ? 'light' : 'dark';
18 | dispatch(setTheme(themeMode));
19 | localStorage.setItem('theme', themeMode);
20 | };
21 |
22 | return (
23 |
24 |
30 |
31 |
32 | );
33 | };
34 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/ToggleSwitch/index.tsx:
--------------------------------------------------------------------------------
1 | import { ToggleSwitch } from './ToggleSwitch';
2 |
3 | export { ToggleSwitch };
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Welcome/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const WelcomeContainer = styled.section`
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | width: 100%;
8 | padding: 7rem 7rem;
9 | `;
10 |
11 | export const WelcomeContent = styled.div`
12 | display: flex;
13 | justify-content: center;
14 | align-items: center;
15 | background: #fff;
16 | @media only screen and (max-width: 1000px) {
17 | flex-direction: column;
18 | }
19 | `;
20 |
21 | export const WelcomeImg = styled.div`
22 | height: 100%;
23 | width: 100%;
24 | display: flex;
25 | justify-content: center;
26 | `;
27 |
28 | export const Img = styled.img`
29 | background-size: cover;
30 | background-position: center;
31 | background-repeat: no-repeat;
32 | object-fit: cover;
33 | border-radius: 4px;
34 | height: 300px;
35 | @media only screen and (min-width: 300px) {
36 | width: 300px;
37 | }
38 | @media only screen and (min-width: 400px) {
39 | width: 400px;
40 | }
41 | @media only screen and (min-width: 600px) {
42 | width: 500px;
43 | }
44 | @media only screen and (min-width: 800px) {
45 | width: 800px;
46 | }
47 | @media only screen and (min-width: 1000px) {
48 | width: 500px;
49 | height: 400px;
50 | }
51 | @media only screen and (min-width: 1200px) {
52 | width: 600px;
53 | }
54 | @media only screen and (min-width: 1500px) {
55 | width: 750px;
56 | }
57 | @media only screen and (min-width: 1800px) {
58 | width: 900px;
59 | }
60 | `;
61 |
62 | export const WelcomeContentText = styled.div`
63 | display: flex;
64 | flex-direction: column;
65 | justify-content: center;
66 | align-items: center;
67 | padding: 3rem;
68 | @media only screen and (min-width: 1200px) {
69 | padding: 3rem 6rem;
70 | }
71 | @media only screen and (min-width: 1500px) {
72 | padding: 3rem 9rem;
73 | }
74 | `;
75 |
76 | export const WelcomeContentTitle = styled.h2`
77 | font-size: 3.6rem;
78 | font-weight: bold;
79 | `;
80 |
81 | export const WelcomeText = styled.p`
82 | font-size: 1.8rem;
83 | &:not(:last-child) {
84 | margin-bottom: 2rem;
85 | }
86 | `;
87 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Welcome/Welcome.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | WelcomeContainer,
3 | WelcomeContent,
4 | WelcomeImg,
5 | WelcomeContentText,
6 | WelcomeContentTitle,
7 | WelcomeText,
8 | Img,
9 | } from './Styled';
10 | import { images } from '../../config/images'
11 |
12 | export const Welcome: React.FC = () => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 | Welcome
21 |
22 | Food HQ is a great way to make meals at home that are health and
23 | enjoyable. We have a variety of categories to choose from that
24 | help meet your needs in your health journey. You can customize the
25 | ingredients you want to be added in your meal. This can be due to
26 | health resources, you don’t have to use evrything in the list.
27 |
28 |
29 | The order will be sent straight to your door step in less then 1hr
30 | pre-packed for you in portions. Start cooking by folloing the
31 | visual guide in your order and serve your family a health meal.
32 |
33 |
34 |
35 |
36 | );
37 | };
38 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Welcome/index.tsx:
--------------------------------------------------------------------------------
1 | import { Welcome } from './Welcome';
2 |
3 | export { Welcome };
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Works/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { BiCheck, BiDetail, BiDish } from 'react-icons/bi';
3 | import { Container } from '../../GlobalStyles';
4 |
5 | export const WorksContent = styled.section`
6 | display: flex;
7 | justify-content: center;
8 | flex-direction: column;
9 | align-items: center;
10 | padding: 5rem;
11 | `;
12 | export const WorksContainer = styled(Container)`
13 | display: flex;
14 | flex-direction: column;
15 | justify-content: space-between;
16 | align-items: center;
17 | padding: 2rem 8rem;
18 | color: #333333;
19 | ${Container};
20 | `;
21 |
22 | export const WorksTitle = styled.h2`
23 | font-size: clamp(2rem, 8.5vw, 5rem);
24 | font-weight: bold;
25 | @media only screen and (max-width: 900px) {
26 | font-size: clamp(1rem, 6.5vw, 5rem);
27 | }
28 | `;
29 |
30 | export const WorksCardContent = styled.div`
31 | display: flex;
32 | justify-content: space-between;
33 | align-items: center;
34 | margin-top: 5rem;
35 | @media only screen and (max-width: 900px) {
36 | flex-direction: column;
37 | }
38 | `;
39 |
40 | export const WorksCard = styled.div`
41 | display: flex;
42 | flex-direction: column;
43 | align-items: center;
44 | text-align: center;
45 | width: 30rem;
46 | height: 30rem;
47 | background-color: #fff;
48 | box-shadow: 0px 5px 40px rgba(0, 0, 0, 0.19);
49 | border-radius: 20px;
50 | transition: all 0.5s ease;
51 | &:not(:last-child) {
52 | margin-right: 5rem;
53 | @media only screen and (min-width: 1300px) {
54 | margin-right: 10rem;
55 | }
56 | @media only screen and (min-width: 1500px) {
57 | margin-right: 20rem;
58 | }
59 | @media only screen and (max-width: 900px) {
60 | margin-bottom: 10rem;
61 | margin-right: 0;
62 | }
63 | @media only screen and (max-width: 800px) {
64 | box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.12);
65 | }
66 | }
67 | @media only screen and (max-width: 900px) {
68 | width: 50rem;
69 | }
70 | @media only screen and (max-width: 500px) {
71 | width: 30rem;
72 | }
73 | @media only screen and (min-width: 1890px) {
74 | width: 40rem;
75 | }
76 | &:hover {
77 | box-shadow: 0px 10px 80px rgba(0, 0, 0, 0.21);
78 | transform: scale(1.05);
79 | background-color: #e38b06;
80 | color: #fff;
81 | }
82 | `;
83 |
84 | export const WorksIconContainer = styled.div`
85 | width: 9rem;
86 | height: 9rem;
87 | border-radius: 50%;
88 | border: 2px solid #333;
89 | transition: all 0.3s ease-out;
90 | margin-top: 2rem;
91 | display: flex;
92 | justify-content: center;
93 | align-items: center;
94 | `;
95 | export const WorksIcon1 = styled(BiCheck)`
96 | color: #333;
97 | font-size: 8rem;
98 | `;
99 |
100 | export const WorksIcon2 = styled(BiDetail)`
101 | color: #333;
102 | font-size: 6rem;
103 | `;
104 | export const WorksIcon3 = styled(BiDish)`
105 | color: #333;
106 | font-size: 6rem;
107 | `;
108 |
109 | export const WorksCardTitle = styled.h3`
110 | font-size: 2.4rem;
111 | font-weight: bold;
112 | padding-top: 1rem;
113 | `;
114 |
115 | export const WorksCardText = styled.p`
116 | font-size: 1.8rem;
117 | padding: 1rem 2rem;
118 | `;
119 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Works/Works.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | WorksContainer,
3 | WorksContent,
4 | WorksTitle,
5 | WorksCardContent,
6 | WorksCard,
7 | WorksIconContainer,
8 | WorksIcon1,
9 | WorksIcon2,
10 | WorksIcon3,
11 | WorksCardTitle,
12 | WorksCardText,
13 | } from './Styled';
14 |
15 | export const Works: React.FC = () => {
16 | return (
17 |
18 |
19 | How it works
20 |
21 |
22 |
23 |
24 |
25 | Pick a meal
26 |
27 | There are different meals every week to choose from. This gives
28 | you a variety of options to switch it up.
29 |
30 |
31 |
32 |
33 |
34 |
35 | Customize it
36 |
37 | Choose your favorite recipes that you want to cook. Pick the
38 | category you love.
39 |
40 |
41 |
42 |
43 |
44 |
45 | Cook it up
46 |
47 | Order the meal you have chosen. Fresh and packed ingredients
48 | straight to your door step.
49 |
50 |
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export default Works;
58 |
--------------------------------------------------------------------------------
/packages/core-ui/src/components/Works/index.tsx:
--------------------------------------------------------------------------------
1 | import { Works } from './Works';
2 |
3 | export { Works };
--------------------------------------------------------------------------------
/packages/core-ui/src/config/env.ts:
--------------------------------------------------------------------------------
1 | const env = process.env;
2 | const {
3 | NODE_ENV,
4 | } = env;
5 |
6 | export const isDevelopment = NODE_ENV === 'development';
--------------------------------------------------------------------------------
/packages/core-ui/src/config/host.ts:
--------------------------------------------------------------------------------
1 | interface Hosts {
2 | featureOne: string;
3 | }
4 |
5 | export default {
6 | featureOne: process.env.REACT_APP_FEATURE_ONE_HOST,
7 | } as Hosts;
8 |
--------------------------------------------------------------------------------
/packages/core-ui/src/config/images.ts:
--------------------------------------------------------------------------------
1 | export const images = {
2 | HeroBg: require('../assets/images/hero-bg.png'),
3 | pizza: require('../assets/images/pizza.png'),
4 | salmon: require('../assets/images/salmon.png'),
5 | };
6 |
--------------------------------------------------------------------------------
/packages/core-ui/src/config/routes.ts:
--------------------------------------------------------------------------------
1 | import { lazy } from 'react';
2 | import { createBrowserHistory } from 'history';
3 | import { routes as routes_config, IRoutesConfig } from './routes_config';
4 |
5 | export const history = createBrowserHistory();
6 |
7 | export const routes: IRoutesConfig = {
8 | [routes_config.index.id]: {
9 | ...routes_config.index,
10 | component: lazy(() => import('../pages/Home'))
11 | },
12 | [routes_config.featureOneApp.id]: {
13 | ...routes_config.featureOneApp,
14 | component: lazy(() => import('../micro-frontend/FeatureOne'))
15 | },
16 | [routes_config.about.id]: {
17 | ...routes_config.about,
18 | component: lazy(() => import('../pages/About'))
19 | },
20 | [routes_config.recipes.id]: {
21 | ...routes_config.recipes,
22 | component: lazy(() => import('../pages/Recipes'))
23 | },
24 | [routes_config.notFound.id]: {
25 | ...routes_config.notFound,
26 | component: lazy(() => import('../pages/NotFound'))
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/packages/core-ui/src/config/routes_config.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { RouteComponentProps } from 'react-router-dom';
3 |
4 | export type IRoutesConfig = {
5 | [key: string]: {
6 | id: string;
7 | name: string;
8 | description?: string;
9 | path: string;
10 | path_string: (params?: any) => string;
11 | exact: boolean;
12 | isPrivate: boolean;
13 | component?:
14 | | React.ComponentType>
15 | | React.ComponentType;
16 | };
17 | };
18 | /**
19 | * @description the aim to create this config is to have
20 | * a single source of truth for the routes defination.
21 | * the reason we are not importing the components here
22 | * for the property `component` is to avoid circular
23 | * import dependencies error.
24 | * components will be assigned in config/routes.ts
25 | */
26 | export const routes: IRoutesConfig = {
27 | index: {
28 | id: 'index',
29 | name: 'Home',
30 | description: 'Home',
31 | path: '/',
32 | path_string: () => {
33 | return `/`;
34 | },
35 | exact: true,
36 | isPrivate: false,
37 | component: undefined
38 | },
39 | about: {
40 | id: 'about',
41 | name: 'About',
42 | description: 'About',
43 | path: '/about',
44 | path_string: () => {
45 | return `/about`;
46 | },
47 | exact: true,
48 | isPrivate: false,
49 | component: undefined
50 | },
51 | recipes: {
52 | id: 'recipes',
53 | name: 'Recipes',
54 | description: 'Recipes',
55 | path: '/recipes',
56 | path_string: () => {
57 | return `/recipes`;
58 | },
59 | exact: true,
60 | isPrivate: false,
61 | component: undefined
62 | },
63 | featureOneApp: {
64 | id: 'featureOneApp',
65 | name: 'Feature One App',
66 | description: 'Feature One App',
67 | path: '/feature-one',
68 | path_string: () => {
69 | return `/feature-one`;
70 | },
71 | exact: false,
72 | isPrivate: false,
73 | component: undefined
74 | },
75 | notFound: {
76 | id: 'notFound',
77 | name: '404',
78 | description: 'Page not found',
79 | path: '/*',
80 | path_string: () => {
81 | return `/404`;
82 | },
83 | exact: false,
84 | isPrivate: false,
85 | component: undefined
86 | }
87 | };
88 |
--------------------------------------------------------------------------------
/packages/core-ui/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 | import App from './App';
4 | import reportWebVitals from './reportWebVitals';
5 | import { toast } from '@mfe/shared/dist/bundle';
6 | import { Provider } from 'react-redux';
7 | import { Theme as ThemeProvider } from './ThemeProvider';
8 | import store from './store';
9 | import GlobalStyles from './GlobalStyles';
10 | import * as serviceWorkerRegistration from './serviceWorkerRegistration';
11 |
12 | import './scss/index.scss';
13 |
14 | toast.configure({ hideProgressBar: true, autoClose: 3000 });
15 |
16 | ReactDOM.render(
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ,
25 | document.getElementById('root')
26 | );
27 |
28 | // If you want your app to work offline and load faster, you can change
29 | // unregister() to register() below. Note this comes with some pitfalls.
30 | // Learn more about service workers: https://cra.link/PWA
31 | serviceWorkerRegistration.register();
32 |
33 | // If you want to start measuring performance in your app, pass a function
34 | // to log results (for example: reportWebVitals(console.log))
35 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
36 | reportWebVitals();
37 |
--------------------------------------------------------------------------------
/packages/core-ui/src/micro-frontend/FeatureOne.tsx:
--------------------------------------------------------------------------------
1 | import { MicroFrontend } from './MicroFrontend';
2 | import { history as defaultHistory } from '../config/routes';
3 | import hostsConfig from '../config/host';
4 |
5 | interface IProps {
6 | history: Object;
7 | }
8 |
9 | export const FeatureOne: React.FC = (props) => {
10 | const { history } = props;
11 |
12 | return (
13 |
18 | );
19 | };
20 |
21 | export default FeatureOne;
22 |
--------------------------------------------------------------------------------
/packages/core-ui/src/micro-frontend/MicroFrontend.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useCallback } from 'react';
2 | import store from '../store';
3 | import { Loader } from '../components/Loader';
4 | import { isDevelopment } from '../config/env';
5 | import {
6 | ErrorContent,
7 | ErrorContentText,
8 | ErrorContentTitle,
9 | ErrorText,
10 | } from './Styled';
11 |
12 | interface IProps {
13 | history: any;
14 | name: string;
15 | host: string;
16 | }
17 |
18 | export const MicroFrontend: React.FC = (props) => {
19 | const { name, host, history } = props;
20 | const [hasError, setError] = useState(null);
21 | const [loading, setLoading] = useState(true);
22 |
23 | const scriptId = `micro-frontend-script-${name}`;
24 | const containerId = `${name}-container`;
25 | const renderApp = `render${name}`;
26 | const unmountApp = `unmount${name}`;
27 |
28 | const renderMicroFrontend = useCallback(() => {
29 | (window as any)[renderApp] &&
30 | (window as any)[renderApp]({
31 | containerId,
32 | history,
33 | store,
34 | });
35 | (window as any)[renderApp] && setLoading(false);
36 | }, [renderApp, containerId, history]);
37 |
38 | useEffect(() => {
39 | if (document.getElementById(scriptId) && (window as any)[renderApp]) {
40 | renderMicroFrontend();
41 | } else {
42 | fetch(`${host}/asset-manifest.json`)
43 | .then((res) => res.json())
44 | .then((manifest) => {
45 | const promises = Object.keys(manifest.files)
46 | .filter((key) => key.endsWith('.js'))
47 | .reduce((sum, key) => {
48 | sum.push(
49 | new Promise((resolve) => {
50 | const path = `${host}${manifest.files[key]}`;
51 | const script = document.createElement('script');
52 |
53 | if (key === 'main.js') {
54 | script.id = scriptId;
55 | }
56 |
57 | script.onload = () => {
58 | resolve(true);
59 | };
60 |
61 | script.crossOrigin = '';
62 | script.src = path;
63 |
64 | document.head.appendChild(script);
65 | })
66 | );
67 | return sum;
68 | }, [] as any);
69 |
70 | Promise.allSettled(promises).then(() => {
71 | renderMicroFrontend();
72 | });
73 | })
74 | .catch((error) => {
75 | setError(error?.message);
76 | setLoading(false);
77 | });
78 | }
79 |
80 | return () => {
81 | (window as any)[unmountApp] &&
82 | (window as any)[unmountApp](containerId) &&
83 | (window as any)[unmountApp](containerId);
84 | };
85 | }, [renderMicroFrontend, host, scriptId, unmountApp, renderApp, containerId]);
86 |
87 | return (
88 | <>
89 | {loading && }
90 | {hasError && (
91 |
92 |
93 |
94 | {isDevelopment
95 | ? `${name} failed to load. Check if dev-server is running.`
96 | : 'This page failed to load'}
97 |
98 |
99 | {isDevelopment ? hasError : 'Please try again later.'}
100 |
101 |
102 |
103 | )}
104 |
105 | >
106 | );
107 | };
108 |
109 | export default MicroFrontend;
110 |
--------------------------------------------------------------------------------
/packages/core-ui/src/micro-frontend/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const ErrorContent = styled.div`
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | background: #fff;
8 | color: #f54c2e;
9 | @media only screen and (max-width: 1000px) {
10 | flex-direction: column;
11 | }
12 | `;
13 | export const ErrorContentText = styled.div`
14 | display: flex;
15 | flex-direction: column;
16 | justify-content: center;
17 | align-items: center;
18 | padding: 3rem;
19 | @media only screen and (min-width: 1200px) {
20 | padding: 3rem 6rem;
21 | }
22 | @media only screen and (min-width: 1500px) {
23 | padding: 3rem 9rem;
24 | }
25 | `;
26 |
27 | export const ErrorContentTitle = styled.h2`
28 | font-size: 3.6rem;
29 | font-weight: bold;
30 | `;
31 |
32 | export const ErrorText = styled.p`
33 | font-size: 1.8rem;
34 | &:not(:last-child) {
35 | margin-bottom: 2rem;
36 | }
37 | `;
38 |
--------------------------------------------------------------------------------
/packages/core-ui/src/pages/About.tsx:
--------------------------------------------------------------------------------
1 | import { Welcome } from '../components/Welcome';
2 |
3 | const About: React.FC = () => {
4 | return ;
5 | };
6 |
7 | export default About;
8 |
--------------------------------------------------------------------------------
/packages/core-ui/src/pages/Home.tsx:
--------------------------------------------------------------------------------
1 | import { Works } from '../components/Works';
2 |
3 | const Home: React.FC = () => {
4 | return ;
5 | };
6 |
7 | export default Home;
8 |
--------------------------------------------------------------------------------
/packages/core-ui/src/pages/NotFound.tsx:
--------------------------------------------------------------------------------
1 | import { NotFound as NotFoundComponent } from '../components/NotFound';
2 |
3 | const NotFound: React.FC = () => {
4 | return ;
5 | };
6 |
7 | export default NotFound;
--------------------------------------------------------------------------------
/packages/core-ui/src/pages/Recipes.tsx:
--------------------------------------------------------------------------------
1 | import { Recipes as RecipesComponent } from '../components/Recipes';
2 |
3 | const Recipes: React.FC = () => {
4 | return ;
5 | };
6 |
7 | export default Recipes;
8 |
--------------------------------------------------------------------------------
/packages/core-ui/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/core-ui/src/reducers/app.ts:
--------------------------------------------------------------------------------
1 | import * as types from '../actions-types';
2 |
3 | export interface InitialStateType {
4 | themeMode: string,
5 | };
6 |
7 | export const initialState : InitialStateType = {
8 | themeMode: localStorage.getItem('theme') || 'light',
9 | };
10 |
11 | const reducer = (state = initialState, action: any) => {
12 | switch (action.type) {
13 | case types.SET_APP_THEME:
14 | return {
15 | ...state,
16 | themeMode: action.payload,
17 | };
18 | default:
19 | return state;
20 | }
21 | };
22 |
23 | export default reducer;
24 |
--------------------------------------------------------------------------------
/packages/core-ui/src/reducers/counter.ts:
--------------------------------------------------------------------------------
1 | import * as types from '../actions-types';
2 |
3 | export interface InitialStateType {
4 | count: number,
5 | };
6 |
7 | export const initialState : InitialStateType = {
8 | count: 0,
9 | };
10 |
11 | const reducer = (state = initialState, action: any) => {
12 | switch (action.type) {
13 | case types.INCRIMENT_COUNT:
14 | return {
15 | ...state,
16 | count: state.count + 1,
17 | };
18 | case types.DECRIMENT_COUNT:
19 | return {
20 | ...state,
21 | count: state.count - 1,
22 | };
23 | default:
24 | return state;
25 | }
26 | };
27 |
28 | export default reducer;
29 |
--------------------------------------------------------------------------------
/packages/core-ui/src/reducers/index.ts:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import counter from './counter';
3 | import app from './app';
4 |
5 | const staticReducer = {
6 | counter,
7 | app
8 | };
9 |
10 | const createReducer = (asyncReducers = {}) =>
11 | combineReducers({
12 | ...staticReducer,
13 | ...asyncReducers,
14 | });
15 |
16 | export default createReducer;
17 |
--------------------------------------------------------------------------------
/packages/core-ui/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/packages/core-ui/src/scss/App.scss:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
--------------------------------------------------------------------------------
/packages/core-ui/src/scss/index.scss:
--------------------------------------------------------------------------------
1 | @import './App.scss';
2 | @import "react-loader-spinner/dist/loader/css/react-spinner-loader.css";
--------------------------------------------------------------------------------
/packages/core-ui/src/service-worker.ts:
--------------------------------------------------------------------------------
1 | ///
2 | /* eslint-disable no-restricted-globals */
3 |
4 | // This service worker can be customized!
5 | // See https://developers.google.com/web/tools/workbox/modules
6 | // for the list of available Workbox modules, or add any other
7 | // code you'd like.
8 | // You can also remove this file if you'd prefer not to use a
9 | // service worker, and the Workbox build step will be skipped.
10 |
11 | import { clientsClaim } from 'workbox-core';
12 | import { ExpirationPlugin } from 'workbox-expiration';
13 | import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
14 | import { registerRoute } from 'workbox-routing';
15 | import { StaleWhileRevalidate } from 'workbox-strategies';
16 |
17 | declare const self: ServiceWorkerGlobalScope;
18 |
19 | clientsClaim();
20 |
21 | // Precache all of the assets generated by your build process.
22 | // Their URLs are injected into the manifest variable below.
23 | // This variable must be present somewhere in your service worker file,
24 | // even if you decide not to use precaching. See https://cra.link/PWA
25 | precacheAndRoute(self.__WB_MANIFEST);
26 |
27 | // Set up App Shell-style routing, so that all navigation requests
28 | // are fulfilled with your index.html shell. Learn more at
29 | // https://developers.google.com/web/fundamentals/architecture/app-shell
30 | const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$');
31 | registerRoute(
32 | // Return false to exempt requests from being fulfilled by index.html.
33 | ({ request, url }: { request: Request; url: URL }) => {
34 | // If this isn't a navigation, skip.
35 | if (request.mode !== 'navigate') {
36 | return false;
37 | }
38 |
39 | // If this is a URL that starts with /_, skip.
40 | if (url.pathname.startsWith('/_')) {
41 | return false;
42 | }
43 |
44 | // If this looks like a URL for a resource, because it contains
45 | // a file extension, skip.
46 | if (url.pathname.match(fileExtensionRegexp)) {
47 | return false;
48 | }
49 |
50 | // Return true to signal that we want to use the handler.
51 | return true;
52 | },
53 | createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
54 | );
55 |
56 | // An example runtime caching route for requests that aren't handled by the
57 | // precache, in this case same-origin .png requests like those from in public/
58 | registerRoute(
59 | // Add in any other file extensions or routing criteria as needed.
60 | ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'),
61 | // Customize this strategy as needed, e.g., by changing to CacheFirst.
62 | new StaleWhileRevalidate({
63 | cacheName: 'images',
64 | plugins: [
65 | // Ensure that once this runtime cache reaches a maximum size the
66 | // least-recently used images are removed.
67 | new ExpirationPlugin({ maxEntries: 50 }),
68 | ],
69 | })
70 | );
71 |
72 | // This allows the web app to trigger skipWaiting via
73 | // registration.waiting.postMessage({type: 'SKIP_WAITING'})
74 | self.addEventListener('message', (event) => {
75 | if (event.data && event.data.type === 'SKIP_WAITING') {
76 | self.skipWaiting();
77 | }
78 | });
79 |
80 | // Any other custom service worker logic can go here.
81 |
--------------------------------------------------------------------------------
/packages/core-ui/src/serviceWorkerRegistration.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://cra.link/PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
19 | );
20 |
21 | type Config = {
22 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
23 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
24 | };
25 |
26 | export function register(config?: Config) {
27 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
28 | // The URL constructor is available in all browsers that support SW.
29 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
30 | if (publicUrl.origin !== window.location.origin) {
31 | // Our service worker won't work if PUBLIC_URL is on a different origin
32 | // from what our page is served on. This might happen if a CDN is used to
33 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
34 | return;
35 | }
36 |
37 | window.addEventListener('load', () => {
38 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
39 |
40 | if (isLocalhost) {
41 | // This is running on localhost. Let's check if a service worker still exists or not.
42 | checkValidServiceWorker(swUrl, config);
43 |
44 | // Add some additional logging to localhost, pointing developers to the
45 | // service worker/PWA documentation.
46 | navigator.serviceWorker.ready.then(() => {
47 | console.log(
48 | 'This web app is being served cache-first by a service ' +
49 | 'worker. To learn more, visit https://cra.link/PWA'
50 | );
51 | });
52 | } else {
53 | // Is not localhost. Just register service worker
54 | registerValidSW(swUrl, config);
55 | }
56 | });
57 | }
58 | }
59 |
60 | function registerValidSW(swUrl: string, config?: Config) {
61 | navigator.serviceWorker
62 | .register(swUrl)
63 | .then((registration) => {
64 | registration.onupdatefound = () => {
65 | const installingWorker = registration.installing;
66 | if (installingWorker == null) {
67 | return;
68 | }
69 | installingWorker.onstatechange = () => {
70 | if (installingWorker.state === 'installed') {
71 | if (navigator.serviceWorker.controller) {
72 | // At this point, the updated precached content has been fetched,
73 | // but the previous service worker will still serve the older
74 | // content until all client tabs are closed.
75 | console.log(
76 | 'New content is available and will be used when all ' +
77 | 'tabs for this page are closed. See https://cra.link/PWA.'
78 | );
79 |
80 | // Execute callback
81 | if (config && config.onUpdate) {
82 | config.onUpdate(registration);
83 | }
84 | } else {
85 | // At this point, everything has been precached.
86 | // It's the perfect time to display a
87 | // "Content is cached for offline use." message.
88 | console.log('Content is cached for offline use.');
89 |
90 | // Execute callback
91 | if (config && config.onSuccess) {
92 | config.onSuccess(registration);
93 | }
94 | }
95 | }
96 | };
97 | };
98 | })
99 | .catch((error) => {
100 | console.error('Error during service worker registration:', error);
101 | });
102 | }
103 |
104 | function checkValidServiceWorker(swUrl: string, config?: Config) {
105 | // Check if the service worker can be found. If it can't reload the page.
106 | fetch(swUrl, {
107 | headers: { 'Service-Worker': 'script' },
108 | })
109 | .then((response) => {
110 | // Ensure service worker exists, and that we really are getting a JS file.
111 | const contentType = response.headers.get('content-type');
112 | if (
113 | response.status === 404 ||
114 | (contentType != null && contentType.indexOf('javascript') === -1)
115 | ) {
116 | // No service worker found. Probably a different app. Reload the page.
117 | navigator.serviceWorker.ready.then((registration) => {
118 | registration.unregister().then(() => {
119 | window.location.reload();
120 | });
121 | });
122 | } else {
123 | // Service worker found. Proceed as normal.
124 | registerValidSW(swUrl, config);
125 | }
126 | })
127 | .catch(() => {
128 | console.log('No internet connection found. App is running in offline mode.');
129 | });
130 | }
131 |
132 | export function unregister() {
133 | if ('serviceWorker' in navigator) {
134 | navigator.serviceWorker.ready
135 | .then((registration) => {
136 | registration.unregister();
137 | })
138 | .catch((error) => {
139 | console.error(error.message);
140 | });
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/packages/core-ui/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/packages/core-ui/src/store/configureStore.dev.ts:
--------------------------------------------------------------------------------
1 | import thunk from 'redux-thunk';
2 | import { createStore, applyMiddleware } from 'redux';
3 | import { createLogger } from 'redux-logger';
4 | import { composeWithDevTools } from 'redux-devtools-extension';
5 | import createReducer from '../reducers';
6 |
7 | const configureStore = () => {
8 | const store = createStore(
9 | createReducer(),
10 | composeWithDevTools(applyMiddleware(thunk, createLogger()))
11 | );
12 |
13 | store['asyncReducers'] = {};
14 |
15 | store['injectReducer'] = (reducers: any) => {
16 | store['asyncReducers'] = {
17 | ...reducers,
18 | };
19 |
20 | store.replaceReducer(createReducer(store['asyncReducers']));
21 |
22 | return store;
23 | };
24 |
25 | if (module.hot) {
26 | // Enable Webpack hot module replacement for reducers
27 | module.hot.accept('../reducers', () => {
28 | store.replaceReducer(createReducer(store['asyncReducers']));
29 | });
30 | }
31 |
32 | return store;
33 | };
34 |
35 | export default configureStore;
36 |
--------------------------------------------------------------------------------
/packages/core-ui/src/store/configureStore.prod.ts:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import createReducer from '../reducers';
4 |
5 | const configureStore = () => {
6 | const store = createStore(createReducer(), applyMiddleware(thunk));
7 |
8 | store['asyncReducers'] = {};
9 |
10 | store['injectReducer'] = (reducers : any) => {
11 | store['asyncReducers'] = {
12 | ...reducers
13 | };
14 |
15 | store.replaceReducer(createReducer(store['asyncReducers']));
16 |
17 | return store;
18 | };
19 |
20 | return store;
21 | };
22 |
23 | export default configureStore;
24 |
--------------------------------------------------------------------------------
/packages/core-ui/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import configureStoreDev from './configureStore.dev'
2 | import configureStoreProd from './configureStore.prod'
3 |
4 | const configureStore = process.env.NODE_ENV === 'production' ? configureStoreProd : configureStoreDev;
5 |
6 | const store = configureStore();
7 |
8 | export default store;
--------------------------------------------------------------------------------
/packages/core-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": false,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | [
4 | "babel-plugin-styled-components",
5 | {
6 | "namespace": "my-app"
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/.env.development:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | SKIP_PREFLIGHT_CHECK=true
3 | PORT=9692
4 | REACT_APP_FEATURE_ONE_HOST=http://localhost:9692
5 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/.env.production:
--------------------------------------------------------------------------------
1 | GENERATE_SOURCEMAP=false
2 | SKIP_PREFLIGHT_CHECK=true
3 | REACT_APP_FEATURE_ONE_HOST=/feature-one
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /dist
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | #cache
27 | .eslintcache
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mfe/feature-01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "author": "asifvora",
6 | "license": "MIT",
7 | "peerDependencies": {
8 | "react": "^17.0.2",
9 | "react-dom": "^17.0.2",
10 | "react-router-dom": "^5.2.0",
11 | "react-scripts": "4.0.1",
12 | "redux": "^4.1.0",
13 | "react-redux": "^7.2.4",
14 | "redux-devtools-extension": "^2.13.9",
15 | "redux-logger": "^3.0.6",
16 | "redux-thunk": "^2.3.0"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "7.5.5",
20 | "@babel/plugin-proposal-class-properties": "^7.13.0",
21 | "@babel/preset-env": "7.14.1",
22 | "@mfe/shared": "link: ../../shared-components/",
23 | "@testing-library/jest-dom": "^5.11.4",
24 | "@testing-library/react": "^11.1.0",
25 | "@testing-library/user-event": "^12.1.10",
26 | "@types/babel__core": "7.1.14",
27 | "@types/babel__preset-env": "7.9.1",
28 | "@types/jest": "^26.0.15",
29 | "@types/node": "^12.0.0",
30 | "@types/node-sass": "^4.11.1",
31 | "@types/prettier": "^2.2.3",
32 | "@types/react": "^17.0.0",
33 | "@types/react-dom": "^17.0.0",
34 | "@types/react-redux": "^7.1.16",
35 | "@types/react-router-dom": "^5.1.7",
36 | "@types/redux-logger": "^3.0.8",
37 | "@types/testing-library__jest-dom": "^5.9.5",
38 | "husky": "^6.0.0",
39 | "lint-staged": "^10.5.4",
40 | "node-sass": "^4.0.0",
41 | "prettier": "^2.2.1",
42 | "typescript": "^4.0.3",
43 | "typesync": "^0.8.0",
44 | "web-vitals": "^1.0.1"
45 | },
46 | "scripts": {
47 | "postinstall": "typesync",
48 | "start": "react-scripts start",
49 | "build": "react-scripts build",
50 | "build:local": "react-scripts build && cp -r build ../../core-ui/build/feature-one",
51 | "test": "react-scripts test",
52 | "eject": "react-scripts eject"
53 | },
54 | "eslintConfig": {
55 | "extends": [
56 | "react-app",
57 | "react-app/jest"
58 | ]
59 | },
60 | "browserslist": {
61 | "production": [
62 | ">0.2%",
63 | "not dead",
64 | "not op_mini all"
65 | ],
66 | "development": [
67 | "last 1 chrome version",
68 | "last 1 firefox version",
69 | "last 1 safari version"
70 | ]
71 | },
72 | "prettier": {
73 | "singleQuote": true
74 | },
75 | "lint-staged": {
76 | "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
77 | "prettier --write",
78 | "git add"
79 | ]
80 | },
81 | "husky": {
82 | "hooks": {
83 | "pre-commit": "lint-staged"
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/frontend-components/feature-01/public/favicon.ico
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/frontend-components/feature-01/public/logo192.png
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asifvora/react-mfe/d3ba690e30d75aa863dc04735b6d72e2ac62fd4c/packages/frontend-components/feature-01/public/logo512.png
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Router } from 'react-router-dom';
2 | import { Routes } from './Routes/Routes';
3 | import { StyleProvider } from './StyleProvider';
4 | import { createBrowserHistory } from 'history';
5 | import './scss/index.scss';
6 |
7 | const defaultHistory = createBrowserHistory();
8 |
9 | export type IProps = {
10 | history?: any;
11 | scope?: string;
12 | };
13 |
14 | export const App: React.FC = (props) => {
15 | const { history, scope } = props;
16 |
17 | return (
18 |
19 |
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/Routes/Routes.tsx:
--------------------------------------------------------------------------------
1 | import { Route, Switch } from 'react-router-dom';
2 | import Order from '../components/Order';
3 | import WishList from '../components/WishList';
4 | import Alerts from '../components/Alerts';
5 | import { RecipeDetails } from '../components/RecipeDetails';
6 |
7 | export const RoutesList = [
8 | {
9 | path: () => '/feature-one/order',
10 | exact: true,
11 | component: Order,
12 | },
13 | {
14 | path: () => '/feature-one/wish-list',
15 | exact: true,
16 | component: WishList,
17 | },
18 | {
19 | path: () => '/feature-one/alerts',
20 | exact: true,
21 | component: Alerts,
22 | },
23 | {
24 | path: () => '/feature-one/recipe-details/:slug',
25 | exact: true,
26 | component: RecipeDetails,
27 | },
28 | ];
29 |
30 | export const Routes : React.FC = () => {
31 | return (
32 |
33 | {RoutesList.map((values, key) => {
34 | const { exact, path, component: Component } = values;
35 | return ;
36 | })}
37 |
38 | );
39 | };
40 |
41 | export default Routes;
42 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/StyleProvider.tsx:
--------------------------------------------------------------------------------
1 |
2 | import { StyleSheetManager } from 'styled-components';
3 | import extraScopePlugin from 'stylis-plugin-extra-scope';
4 |
5 | Object.defineProperty(extraScopePlugin, 'name', {
6 | value: 'feature-01',
7 | });
8 |
9 | export type IProps = {
10 | scope?: string;
11 | children:any
12 | };
13 |
14 | export const StyleProvider: React.FC = (props) => {
15 | const { scope, children } = props;
16 | const stylisPlugins = scope ? [extraScopePlugin(`${scope}`)] : [];
17 |
18 | return (
19 |
20 | {children}
21 |
22 | );
23 | };
24 |
25 | export default StyleProvider;
26 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/actions-types/index.ts:
--------------------------------------------------------------------------------
1 | export const INCRIMENT_ALERT : String = 'INCRIMENT_ALERT';
2 | export const DECRIMENT_ALERT : String = 'DECRIMENT_ALERT';
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/actions/alerts.ts:
--------------------------------------------------------------------------------
1 | import * as types from '../actions-types';
2 |
3 | export const incriment = () => ({
4 | type: types.INCRIMENT_ALERT,
5 | });
6 |
7 | export const decrement = () => ({
8 | type: types.DECRIMENT_ALERT,
9 | });
10 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/components/Alerts.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dispatch } from 'redux';
3 | import { useSelector, shallowEqual, useDispatch } from 'react-redux';
4 | import { incriment, decrement } from '../actions/alerts';
5 | import { Button } from '@mfe/shared/dist/bundle';
6 |
7 | export const Alerts: React.FC = () => {
8 | const dispatch: Dispatch = useDispatch();
9 | const count: number = useSelector(
10 | (state: any) => state.alerts.count,
11 | shallowEqual
12 | );
13 |
14 | const onIncriment = () => {
15 | dispatch(incriment());
16 | };
17 |
18 | const onDecrement = () => {
19 | dispatch(decrement());
20 | };
21 |
22 | return (
23 |
24 |
25 |
Alerts is :{count}
26 |
27 |
28 | );
29 | };
30 |
31 | export default Alerts;
32 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/components/Order.tsx:
--------------------------------------------------------------------------------
1 | export const Order: React.FC = () => {
2 | return feature one app Order component
;
3 | };
4 |
5 | export default Order;
6 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/components/RecipeDetails/RecipeDetails.tsx:
--------------------------------------------------------------------------------
1 | import { OutlineButton, Container } from '@mfe/shared/dist/bundle';
2 | import { images } from '../../config/images';
3 | import host from '../../config/host';
4 | import {
5 | MealsContainer,
6 | MealsWrapper,
7 | MealsImage,
8 | Img,
9 | MealsContent,
10 | MealsContentTitle,
11 | MealsContentText,
12 | MealsContentItems,
13 | MealsContentList,
14 | MealsContentDetails,
15 | MealsContentIcon,
16 | MealsContentCategory,
17 | MealsContentBtn,
18 | } from './Styled';
19 |
20 | export const RecipeDetails: React.FC = () => {
21 | return (
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 | Personalize your Meals
33 |
34 | Choose your weekly or daily meal plan from our meals to kick start
35 | your month. All meanu are fresh and set for you in portion to make
36 | it easier to cook immediately.
37 |
38 |
39 |
40 |
41 |
42 | Vegetarian
43 |
44 |
45 |
46 | Gluten-Free
47 |
48 |
49 |
50 | Card-conscious
51 |
52 |
53 |
54 |
55 | Calorie -conscious
56 |
57 |
58 |
59 |
60 |
61 |
62 | 15 mins pre-kit
63 |
64 |
65 |
66 | Featured Meals
67 |
68 |
69 |
70 | New recipes
71 |
72 |
73 |
74 | Low fat meals
75 |
76 |
77 |
78 |
79 |
80 | Explore Our Meals
81 |
82 |
83 |
84 |
85 |
86 |
87 | );
88 | };
89 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/components/RecipeDetails/Styled.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 | import { BiCheck } from 'react-icons/bi';
3 |
4 | export const MealsContainer = styled.section`
5 | display: flex;
6 | align-items: center;
7 | margin: 50px;
8 | `;
9 |
10 | export const MealsWrapper = styled.div`
11 | display: flex;
12 | justify-content: center;
13 | align-items: flex-start;
14 | overflow: hidden;
15 | @media only screen and (max-width: 1000px) {
16 | flex-direction: column;
17 | }
18 | @media only screen and (min-width: 1370px) {
19 | align-items: center;
20 | }
21 | `;
22 |
23 | export const MealsImage = styled.div`
24 | width: 100%;
25 | height: 100%;
26 | @media only screen and (max-width: 1000px) {
27 | display: flex;
28 | justify-content: center;
29 | }
30 | `;
31 |
32 | export const Img = styled.img`
33 | @media only screen and (min-width: 1800px) {
34 | width: 900px;
35 | }
36 | @media only screen and (max-width: 1400px) {
37 | width: 650px;
38 | }
39 | @media only screen and (max-width: 1200px) {
40 | width: 500px;
41 | }
42 | @media only screen and (max-width: 1000px) {
43 | width: 700px;
44 | height: 500px;
45 | }
46 | @media only screen and (max-width: 700px) {
47 | width: 600px;
48 | }
49 | @media only screen and (max-width: 500px) {
50 | width: 400px;
51 | height: 300px;
52 | }
53 | @media only screen and (max-width: 400px) {
54 | width: 350px;
55 | }
56 | `;
57 |
58 | export const MealsContent = styled.div`
59 | padding: 0 10rem;
60 | @media only screen and (min-width: 1800px) {
61 | margin: 0 20rem;
62 | }
63 | @media only screen and (max-width: 1300px) {
64 | padding: 2rem 5rem;
65 | }
66 | @media only screen and (max-width: 1000px) {
67 | text-align: center;
68 | }
69 | @media only screen and (max-width: 500px) {
70 | padding: 2rem;
71 | }
72 | `;
73 |
74 | export const MealsContentTitle = styled.h2`
75 | font-size: clamp(2rem, 8vw, 5rem);
76 | margin-bottom: 3rem;
77 | text-align: center;
78 | line-height: 1.2;
79 | `;
80 |
81 | export const MealsContentText = styled.p`
82 | font-size: 1.8rem;
83 | @media only screen and (max-width: 1000px) {
84 | font-size: 2rem;
85 | }
86 | `;
87 |
88 | export const MealsContentItems = styled.div`
89 | display: flex;
90 | margin-bottom: 3rem;
91 | margin-top: 3rem;
92 | @media only screen and (max-width: 1000px) {
93 | justify-content: center;
94 | }
95 | `;
96 |
97 | export const MealsContentList = styled.div`
98 | display: flex;
99 | flex-direction: column;
100 | align-items: flex-start;
101 | &:not(:last-child) {
102 | margin-right: 10rem;
103 | }
104 | @media only screen and (max-width: 500px) {
105 | &:not(:last-child) {
106 | margin-right: 3rem;
107 | }
108 | }
109 | `;
110 |
111 | export const MealsContentDetails = styled.div`
112 | display: flex;
113 | justify-content: center;
114 | align-items: center;
115 | `;
116 | export const MealsContentIcon = styled(BiCheck)`
117 | font-size: 1.6rem;
118 | color: #e38b06;
119 | margin-right: 1rem;
120 | `;
121 |
122 | export const MealsContentCategory = styled.h3`
123 | font-weight: 400;
124 | font-size: 1.8rem;
125 | @media only screen and (max-width: 450px) {
126 | font-size: 1.7rem;
127 | }
128 | @media only screen and (max-width: 340px) {
129 | font-size: 1.5rem;
130 | }
131 | @media only screen and (max-width: 335px) {
132 | font-size: 1.2rem;
133 | }
134 | `;
135 |
136 | export const MealsContentBtn = styled.div`
137 | display: flex;
138 | align-items: center;
139 | text-decoration: none;
140 | @media only screen and (max-width: 1000px) {
141 | justify-content: center;
142 | }
143 | `;
144 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/components/RecipeDetails/index.tsx:
--------------------------------------------------------------------------------
1 | import { RecipeDetails } from './RecipeDetails';
2 |
3 | export { RecipeDetails };
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/components/WishList.tsx:
--------------------------------------------------------------------------------
1 | export const WishList: React.FC = () => {
2 | return feature one app WishList component
;
3 | };
4 |
5 | export default WishList;
6 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/config/host.ts:
--------------------------------------------------------------------------------
1 | interface Hosts {
2 | featureOne: string;
3 | }
4 |
5 | export default {
6 | featureOne: process.env.REACT_APP_FEATURE_ONE_HOST,
7 | } as Hosts;
8 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/config/images.ts:
--------------------------------------------------------------------------------
1 | export const images = {
2 | bigChicken: require('../assets/images/Big-chicken.svg'),
3 | };
4 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import reportWebVitals from './reportWebVitals';
5 | import { Provider } from 'react-redux';
6 | import { reducerMap } from './reducers';
7 | import configureStore from './store';
8 |
9 | const store = configureStore();
10 |
11 | declare global {
12 | interface Window {
13 | renderFeatureOne: any;
14 | unmountFeatureOne: any;
15 | }
16 | }
17 |
18 | interface IProps {
19 | containerId?: string;
20 | history?: any;
21 | store?: any;
22 | }
23 |
24 | const FeatureOneApp: React.FC = (props) => {
25 | const { history, store } = props;
26 | const [renderApp, setRenderApp] = useState(false);
27 |
28 | useEffect(() => {
29 | store['injectReducer'](reducerMap);
30 | setRenderApp(true);
31 | }, [store]);
32 |
33 | return renderApp ? (
34 |
35 |
36 |
37 | ) : null;
38 | };
39 |
40 | // render micro frontend
41 | window.renderFeatureOne = ({ containerId, history, store }) => {
42 | ReactDOM.render(
43 | ,
44 | document.getElementById(containerId)
45 | );
46 | };
47 |
48 | // unmount micro frontend
49 | window.unmountFeatureOne = (containerId) => {
50 | const element = document.getElementById(containerId);
51 | element && ReactDOM.unmountComponentAtNode(element);
52 | };
53 |
54 | // Mount to root if it is not a micro frontend
55 | if (!document.getElementById('FeatureOne-container')) {
56 | ReactDOM.render(
57 |
58 |
59 |
60 |
61 | ,
62 | document.getElementById('root')
63 | );
64 | }
65 |
66 | // If you want to start measuring performance in your app, pass a function
67 | // to log results (for example: reportWebVitals(console.log))
68 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
69 | reportWebVitals();
70 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/reducers/alerts.ts:
--------------------------------------------------------------------------------
1 | import * as types from '../actions-types';
2 |
3 | export interface InitialStateType {
4 | count: number,
5 | };
6 |
7 | export const initialState : InitialStateType = {
8 | count: 0,
9 | };
10 |
11 | const reducer = (state = initialState, action : any) => {
12 | switch (action.type) {
13 | case types.INCRIMENT_ALERT:
14 | return {
15 | ...state,
16 | count: state.count + 1,
17 | };
18 | case types.DECRIMENT_ALERT:
19 | return {
20 | ...state,
21 | count: state.count - 1,
22 | };
23 | default:
24 | return state;
25 | }
26 | };
27 |
28 | export default reducer;
29 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/reducers/index.ts:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import alerts from './alerts';
3 |
4 | export const reducerMap = {
5 | alerts,
6 | };
7 |
8 | const rootReducer = combineReducers({
9 | ...reducerMap,
10 | });
11 |
12 | export default rootReducer;
13 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/scss/App.scss:
--------------------------------------------------------------------------------
1 | .title {
2 | text-align: center;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | justify-content: center;
7 | font-size: calc(10px + 2vmin);
8 | color: #61dafb;
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/scss/index.scss:
--------------------------------------------------------------------------------
1 | @import './App.scss';
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/store/configureStore.dev.ts:
--------------------------------------------------------------------------------
1 | import thunk from 'redux-thunk';
2 | import { createStore, applyMiddleware } from 'redux';
3 | import { createLogger } from 'redux-logger';
4 | import { composeWithDevTools } from 'redux-devtools-extension';
5 | import rootReducer from '../reducers';
6 |
7 | const configureStore = () => {
8 | const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk, createLogger())));
9 |
10 | if (module.hot) {
11 | // Enable Webpack hot module replacement for reducers
12 | module.hot.accept('../reducers', () => {
13 | store.replaceReducer(rootReducer);
14 | });
15 | }
16 |
17 | return store;
18 | };
19 |
20 | export default configureStore;
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/store/configureStore.prod.ts:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import rootReducer from '../reducers';
4 |
5 | const configureStore = () => createStore(rootReducer, applyMiddleware(thunk));
6 |
7 | export default configureStore;
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import configureStoreDev from './configureStore.dev'
2 | import configureStoreProd from './configureStore.prod'
3 |
4 | const configureStore = process.env.NODE_ENV === 'production' ? configureStoreProd : configureStoreDev;
5 |
6 | export default configureStore
--------------------------------------------------------------------------------
/packages/frontend-components/feature-01/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": false,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/packages/shared-components/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "babel-plugin-require-context-hook"
4 | ]
5 | }
--------------------------------------------------------------------------------
/packages/shared-components/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
--------------------------------------------------------------------------------
/packages/shared-components/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /dist
14 | /storybook-static
15 |
16 | # misc
17 | .DS_Store
18 | .env.local
19 | .env.development.local
20 | .env.test.local
21 | .env.production.local
22 |
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | #cache
28 | .eslintcache
--------------------------------------------------------------------------------
/packages/shared-components/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: ['../src/**/*.stories.tsx'],
3 | addons: [
4 | '@storybook/addon-links',
5 | '@storybook/addon-essentials',
6 | '@storybook/preset-create-react-app',
7 | ],
8 | typescript: {
9 | reactDocgen: 'react-docgen-typescript',
10 | reactDocgenTypescriptOptions: {
11 | compilerOptions: {
12 | allowSyntheticDefaultImports: false,
13 | esModuleInterop: false,
14 | },
15 | }
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/packages/shared-components/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/packages/shared-components/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mfe/shared",
3 | "version": "0.1.0",
4 | "private": true,
5 | "author": "asifvora",
6 | "license": "MIT",
7 | "peerDependencies": {
8 | "axios": "^0.21.1",
9 | "react": "^17.0.2",
10 | "react-dom": "^17.0.2",
11 | "react-scripts": "4.0.1",
12 | "react-toastify": "^7.0.4"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "7.5.5",
16 | "@babel/plugin-proposal-class-properties": "^7.13.0",
17 | "@babel/preset-env": "7.14.1",
18 | "@rollup/plugin-typescript": "^8.2.1",
19 | "@storybook/addon-actions": "^5.2.6",
20 | "@storybook/addon-docs": "^6.2.9",
21 | "@storybook/addon-links": "^5.2.6",
22 | "@storybook/addons": "^5.2.6",
23 | "@storybook/preset-create-react-app": "^3.1.7",
24 | "@storybook/react": "^6.2.9",
25 | "@testing-library/jest-dom": "^5.11.4",
26 | "@testing-library/react": "^11.1.0",
27 | "@testing-library/user-event": "^12.1.10",
28 | "@types/babel__core": "7.1.14",
29 | "@types/babel__preset-env": "7.9.1",
30 | "@types/jest": "^26.0.15",
31 | "@types/node": "^12.0.0",
32 | "@types/node-sass": "^4.11.1",
33 | "@types/prettier": "^2.2.3",
34 | "@types/react": "^17.0.0",
35 | "@types/react-dom": "^17.0.0",
36 | "@types/rollup-plugin-json": "^3.0.2",
37 | "@types/styled-components": "^5.1.9",
38 | "@types/testing-library__jest-dom": "^5.9.5",
39 | "babel-plugin-require-context-hook": "^1.0.0",
40 | "husky": "^6.0.0",
41 | "lint-staged": "^10.5.4",
42 | "node-sass": "^4.0.0",
43 | "prettier": "^2.2.1",
44 | "rollup": "^1.23.1",
45 | "rollup-plugin-babel": "^4.4.0",
46 | "rollup-plugin-bundle-scss": "0.1.0",
47 | "rollup-plugin-cleaner": "^1.0.0",
48 | "rollup-plugin-commonjs": "^10.1.0",
49 | "rollup-plugin-json": "^4.0.0",
50 | "rollup-plugin-multi-entry": "2.1.0",
51 | "rollup-plugin-node-externals": "^2.2.0",
52 | "rollup-plugin-node-resolve": "^5.2.0",
53 | "rollup-plugin-typescript": "^1.0.1",
54 | "typescript": "^4.0.3",
55 | "typesync": "^0.8.0"
56 | },
57 | "scripts": {
58 | "postinstall": "typesync",
59 | "build": "rollup -c rollup.config.ts",
60 | "test": "react-scripts test",
61 | "storybook": "start-storybook -p 9009 -s public",
62 | "build-storybook": "build-storybook -s public"
63 | },
64 | "eslintConfig": {
65 | "extends": [
66 | "react-app",
67 | "react-app/jest"
68 | ]
69 | },
70 | "browserslist": {
71 | "production": [
72 | ">0.2%",
73 | "not dead",
74 | "not op_mini all"
75 | ],
76 | "development": [
77 | "last 1 chrome version",
78 | "last 1 firefox version",
79 | "last 1 safari version"
80 | ]
81 | },
82 | "prettier": {
83 | "singleQuote": true
84 | },
85 | "lint-staged": {
86 | "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
87 | "prettier --write",
88 | "git add"
89 | ]
90 | },
91 | "husky": {
92 | "hooks": {
93 | "pre-commit": "lint-staged"
94 | }
95 | },
96 | "dependencies": {
97 | "styled-components": "^5.3.0",
98 | "typescript-styled-plugin": "^0.16.0"
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/packages/shared-components/rollup.config.ts:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import commonjs from 'rollup-plugin-commonjs';
3 | import resolve from 'rollup-plugin-node-resolve';
4 | import externals from 'rollup-plugin-node-externals';
5 | import json from 'rollup-plugin-json';
6 | import tsPlugin from '@rollup/plugin-typescript';
7 | import cleaner from 'rollup-plugin-cleaner';
8 | import bundleScss from 'rollup-plugin-bundle-scss';
9 |
10 | const commonPlugins = [
11 | json({}),
12 | babel({
13 | exclude: /node_modules/,
14 | runtimeHelpers: true,
15 | presets: [['@babel/preset-env', { modules: false }]],
16 | plugins: ['@babel/plugin-proposal-class-properties'],
17 | }),
18 | commonjs({
19 | ignoreGlobal: true,
20 | include: /\/node_modules\//,
21 | namedExports: {
22 | react: Object.keys(require('react')),
23 | 'react-is': Object.keys(require('react-is')),
24 | },
25 | }),
26 | externals(),
27 | resolve({
28 | browser: true,
29 | }),
30 | tsPlugin(),
31 | ];
32 |
33 | const config = [
34 | {
35 | input: './src/bundle.ts',
36 | plugins: [
37 | cleaner({
38 | targets: ['./dist/'],
39 | }),
40 | ...commonPlugins,
41 | ],
42 | output: {
43 | dir: 'dist',
44 | format: 'esm',
45 | exports: 'named',
46 | },
47 | },
48 | {
49 | input: './src/scss/index.scss',
50 | plugins: [
51 | bundleScss({
52 | output: 'bundle.scss',
53 | }),
54 | ],
55 | output: {
56 | file: 'dist/dummy.js',
57 | },
58 | },
59 | ];
60 |
61 | export default config;
62 |
--------------------------------------------------------------------------------
/packages/shared-components/src/bundle.ts:
--------------------------------------------------------------------------------
1 | import { toast, successToast, infoToast, errorToast } from './services/Toast';
2 | import { Button } from './components/Button';
3 | import { OutlineButton } from './components/OutlineButton';
4 | import { Container } from './components/Container';
5 |
6 | export {
7 | toast,
8 | successToast,
9 | infoToast,
10 | errorToast,
11 | Button,
12 | OutlineButton,
13 | Container,
14 | };
15 |
--------------------------------------------------------------------------------
/packages/shared-components/src/components/Button/Button.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import { Button } from './Button';
4 |
5 | test('renders Button text', () => {
6 | render();
7 | const linkElement = screen.getByText(/Click on me/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/packages/shared-components/src/components/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled, { css } from 'styled-components';
3 |
4 | export type IStyledButtonProps = {
5 | primary?: boolean;
6 | };
7 |
8 | export const StyledButton = styled.button`
9 | background: transparent;
10 | border-radius: 3px;
11 | border: 2px solid palevioletred;
12 | color: #fa0758;
13 | margin: 0.5em 1em;
14 | padding: 0.25em 1em;
15 | cursor: pointer;
16 |
17 | ${props => props.primary && css`
18 | background: #090643;
19 | color: white;
20 | `}
21 | `;
22 |
23 |
24 | export type IButtonProps = {
25 | onClick?: () => void;
26 | };
27 |
28 | export const Button: React.FC = props => {
29 | return (
30 | {props.children}
31 | );
32 | };
--------------------------------------------------------------------------------
/packages/shared-components/src/components/Button/index.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from './Button';
2 |
3 | export { Button };
--------------------------------------------------------------------------------
/packages/shared-components/src/components/Container/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | margin: 0 auto;
5 | padding: 0 50px;
6 | max-width: 1300px;
7 | width: 100%;
8 | @media (max-width: 400px) {
9 | padding: 0 10px;
10 | }
11 | @media (max-width: 991px) {
12 | padding: 0 30px;
13 | }
14 | @media (min-width: 1500px) {
15 | max-width: 1500px;
16 | }
17 | @media (min-width: 1800px) {
18 | max-width: 1800px;
19 | padding: 0 30px;
20 | }
21 | `;
22 |
--------------------------------------------------------------------------------
/packages/shared-components/src/components/OutlineButton/index.tsx:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export type IStyledOutlineButtonProps = {
4 | bigRadius?: boolean;
5 | big?: boolean;
6 | fontBig?: boolean;
7 | bigFont?: boolean;
8 | };
9 |
10 | export const OutlineButton = styled.button`
11 | border-radius: ${({ bigRadius }) => (bigRadius ? '40px' : '30px')};
12 | border: 2px solid #333;
13 | color: #333;
14 | outline: none;
15 | padding: ${({ big }) => (big ? '15px 60px' : '13px 55px')};
16 | font-size: ${({ fontBig }) => (fontBig ? '22px' : '18px')};
17 | transition: all 0.5s ease;
18 | background-color: #fefefe;
19 | &:hover {
20 | background-color: #333;
21 | color: #fff;
22 | border: none;
23 | transform: translateY(-0.5rem) scale(1.02);
24 | }
25 | &:active {
26 | transform: translateY(0.5rem);
27 | }
28 | @media only screen and (max-width: 1200px) {
29 | border-radius: ${({ bigRadius }) => (bigRadius ? '20px' : '18px')};
30 | padding: ${({ big }) => (big ? '9px 30px' : '8px 28px')};
31 | font-size: ${({ fontBig }) => (fontBig ? '18px' : '16px')};
32 | }
33 | @media only screen and (max-width: 780px) {
34 | border: none;
35 | color: #e38b06;
36 | padding: 1rem 2rem;
37 | background: none;
38 | transition: all 0.4s ease;
39 | &:hover {
40 | border-bottom: 1px solid #e38b06;
41 | background: none;
42 | border-radius: 0;
43 | color: #e38b06;
44 | }
45 | }
46 | `;
47 |
--------------------------------------------------------------------------------
/packages/shared-components/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/shared-components/src/scss/App.scss:
--------------------------------------------------------------------------------
1 | .title {
2 | text-align: center;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | justify-content: center;
7 | font-size: calc(10px + 2vmin);
8 | color: #61dafb;
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/packages/shared-components/src/scss/index.scss:
--------------------------------------------------------------------------------
1 | @import './App.scss';
--------------------------------------------------------------------------------
/packages/shared-components/src/services/HttpService.ts:
--------------------------------------------------------------------------------
1 | import axios, { Method } from 'axios';
2 | import { errorToast } from './Toast'
3 |
4 | const BASE_URL = process.env.REACT_APP_API_URL;
5 |
6 | const codes = {
7 | UNAUTHORIZED: 401,
8 | CUSTOM_TOKEN_EXPIRED: -2,
9 | REQUEST_TIMEOUT: 408,
10 | ECONNABORTED: 'ECONNABORTED'
11 | };
12 |
13 | axios.interceptors.response.use(
14 | response => response,
15 | error => {
16 | if (error?.response?.data?.error?.code === codes.CUSTOM_TOKEN_EXPIRED) {
17 | console.log('token expired');
18 | }
19 |
20 | if (
21 | error?.response?.status === codes.REQUEST_TIMEOUT ||
22 | error.code === codes.ECONNABORTED
23 | ) {
24 | //Looks like the server is taking to long to respond, please try again in sometime.
25 | console.log(`A timeout happend on url ${error.config.url}`);
26 | errorToast({ content: 'Server request timed out. Please retry again.' })
27 | }
28 |
29 | if (!error?.response?.data?.error) {
30 | console.log('Server error not found');
31 | //Add something went wrong toast error here
32 | //statusText in toast maybe.
33 | }
34 | return Promise.reject(error);
35 | }
36 | );
37 |
38 | const getFullUrl = (url: string) => {
39 | return `${BASE_URL}${url}`;
40 | };
41 |
42 | export type Irequest = {
43 | subUrl: string;
44 | method?: Method;
45 | data?: object;
46 | params?: object;
47 | headers?: object;
48 | };
49 |
50 | export const GET = (request: Irequest) => {
51 | return commonFetch({ method: 'get', ...request });
52 | };
53 |
54 | export const POST = (request: Irequest) => {
55 | return commonFetch({ method: 'post', ...request });
56 | };
57 |
58 | export const PUT = (request: Irequest) => {
59 | return commonFetch({ method: 'put', ...request });
60 | };
61 |
62 | export const PATCH = (request: Irequest) => {
63 | return commonFetch({ method: 'patch', ...request });
64 | };
65 |
66 | export const DELETE = (request: Irequest) => {
67 | return commonFetch({ method: 'delete', ...request });
68 | };
69 |
70 | const commonFetch = (request: Irequest) => {
71 | const { subUrl, method, data = {}, params, headers = {} } = request;
72 | const url = getFullUrl(subUrl);
73 | const commonHeaders = getCommonHeaders();
74 |
75 | return axios({
76 | method,
77 | url,
78 | params,
79 | data,
80 | headers: { ...commonHeaders, ...headers }
81 | });
82 | };
83 |
84 | export const contentTypes = {
85 | multipart: {
86 | 'Content-Type': 'multipart/form-data'
87 | },
88 | json: {
89 | 'Content-Type': 'application/json'
90 | }
91 | };
92 |
93 | const getCommonHeaders = () => {
94 | return {
95 | ...contentTypes.json
96 | //Authorization: `JWT ${token}`,
97 | };
98 | };
99 |
--------------------------------------------------------------------------------
/packages/shared-components/src/services/Toast.ts:
--------------------------------------------------------------------------------
1 | import { toast, ToastOptions } from 'react-toastify';
2 | import 'react-toastify/dist/ReactToastify.css';
3 |
4 | type IParams = {
5 | content: string;
6 | options?: ToastOptions;
7 | };
8 |
9 | export const errorToast = ({ content, options = {} }: IParams) => {
10 | if (!content) {
11 | return;
12 | }
13 | return toast(content, {
14 | type: 'error',
15 | ...options
16 | });
17 | };
18 |
19 | export const successToast = ({ content, options = {} }: IParams) => {
20 | if (!content) {
21 | return;
22 | }
23 | return toast(content, {
24 | type: 'success',
25 | ...options
26 | });
27 | };
28 |
29 | export const infoToast = ({ content, options = {} }: IParams) => {
30 | if (!content) {
31 | return;
32 | }
33 | return toast(content, {
34 | type: 'info',
35 | ...options
36 | });
37 | };
38 |
39 | export { toast };
40 |
--------------------------------------------------------------------------------
/packages/shared-components/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/packages/shared-components/src/stories/0-Welcome.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { linkTo } from '@storybook/addon-links';
3 | import { Welcome } from '@storybook/react/demo';
4 |
5 | type ITypes = {
6 | title: string;
7 | };
8 |
9 | const welcome: ITypes = {
10 | title: 'Welcome',
11 | };
12 |
13 | export default welcome;
14 |
15 | export const toStorybook = () => ;
16 |
17 | toStorybook.story = {
18 | name: 'to Storybook',
19 | };
20 |
--------------------------------------------------------------------------------
/packages/shared-components/src/stories/1-Button.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { action } from '@storybook/addon-actions';
3 | import { Button } from '@storybook/react/demo';
4 |
5 | type ITypes = {
6 | title: string;
7 | };
8 |
9 | const welcome: ITypes = {
10 | title: 'Button',
11 | };
12 |
13 | export default welcome;
14 |
15 | export const text = () => (
16 |
17 | );
18 |
19 | export const emoji = () => (
20 |
25 | );
26 |
--------------------------------------------------------------------------------
/packages/shared-components/src/utils/commonFunction.ts:
--------------------------------------------------------------------------------
1 | export const getApiErrorMessage = (error: any) => {
2 | return error?.response?.data?.message;
3 | };
--------------------------------------------------------------------------------
/packages/shared-components/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx",
22 | "plugins": [
23 | {
24 | "name": "typescript-styled-plugin"
25 | }
26 | ]
27 | },
28 | "include": [
29 | "src"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "noImplicitAny": false,
6 | "removeComments": true,
7 | "noLib": false,
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es6",
11 | "sourceMap": true,
12 | "lib": ["es6"]
13 | },
14 | "exclude": ["node_modules", "**/*.spec.ts"]
15 | }
16 |
--------------------------------------------------------------------------------