├── .gitignore
├── packages
├── container
│ ├── src
│ │ ├── index.js
│ │ ├── bootstrap.js
│ │ ├── App.js
│ │ └── components
│ │ │ ├── MarketingApp.js
│ │ │ └── Header.js
│ ├── public
│ │ └── index.html
│ ├── config
│ │ ├── webpack.common.js
│ │ ├── webpack.dev.js
│ │ └── webpack.prod.js
│ └── package.json
├── marketing
│ ├── src
│ │ ├── index.js
│ │ ├── App.js
│ │ ├── bootstrap.js
│ │ └── components
│ │ │ ├── Landing.js
│ │ │ └── Pricing.js
│ ├── public
│ │ └── index.html
│ ├── config
│ │ ├── webpack.common.js
│ │ ├── webpack.prod.js
│ │ └── webpack.dev.js
│ └── package.json
├── auth
│ └── package.json
└── dashboard
│ └── package.json
└── .github
└── workflows
├── marketing.yml
└── container.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
--------------------------------------------------------------------------------
/packages/container/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
--------------------------------------------------------------------------------
/packages/marketing/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
--------------------------------------------------------------------------------
/packages/container/src/bootstrap.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import App from "./App";
5 |
6 | ReactDOM.render( , document.querySelector("#root"));
--------------------------------------------------------------------------------
/packages/container/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Container App
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/marketing/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Marketing
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/marketing/config/webpack.common.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | module: {
3 | rules: [
4 | {
5 | test: /\.m?js$/,
6 | exclude: /(node_modules)/,
7 | use: {
8 | loader: 'babel-loader',
9 | options: {
10 | presets: ['@babel/preset-react', '@babel/preset-env'],
11 | plugins: ['@babel/plugin-transform-runtime']
12 | }
13 | }
14 | }
15 | ]
16 | }
17 | }
--------------------------------------------------------------------------------
/packages/container/config/webpack.common.js:
--------------------------------------------------------------------------------
1 | const HTMLWebpackPlugin = require('html-webpack-plugin');
2 |
3 |
4 | module.exports = {
5 | module: {
6 | rules: [
7 | {
8 | test: /\.m?js$/,
9 | exclude: /(node_modules)/,
10 | use: {
11 | loader: 'babel-loader',
12 | options: {
13 | presets: ['@babel/preset-react', '@babel/preset-env'],
14 | plugins: ['@babel/plugin-transform-runtime']
15 | }
16 | }
17 | }
18 | ]
19 | },
20 | plugins: [
21 | new HTMLWebpackPlugin({
22 | template: './public/index.html',
23 | }),
24 | ],
25 | }
--------------------------------------------------------------------------------
/packages/container/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter, BrowserRouterProps } from 'react-router-dom';
3 | import { StylesProvider, createGenerateClassName } from '@material-ui/core/styles';
4 | import MarketingApp from './components/MarketingApp';
5 | import Header from './components/Header';
6 |
7 | const generateClassName = createGenerateClassName({
8 | productionPrefix: "co",
9 | })
10 |
11 | export default () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | )
22 | }
--------------------------------------------------------------------------------
/packages/container/config/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
3 | const commonConfig = require('./webpack.common.js');
4 | const packageJson = require('../package.json');
5 |
6 |
7 | const devConfig = {
8 | mode: 'development',
9 | devServer: {
10 | port: 8080,
11 | historyApiFallback: {
12 | index: 'index.html'
13 | }
14 | },
15 | plugins: [
16 | new ModuleFederationPlugin({
17 | name: 'container',
18 | remotes: {
19 | 'marketing': 'marketing@http://localhost:8081/remoteEntry.js'
20 | },
21 | shared: packageJson.dependencies
22 | })
23 | ]
24 | }
25 |
26 | module.exports = merge(commonConfig, devConfig);
--------------------------------------------------------------------------------
/packages/marketing/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Switch, Route, Router } from "react-router-dom";
3 | import { StylesProvider, createGenerateClassName } from "@material-ui/core/styles";
4 |
5 | import Landing from "./components/Landing";
6 | import Pricing from "./components/Pricing";
7 |
8 | const generateClassName = createGenerateClassName({
9 | productionPrefix: "ma",
10 | })
11 |
12 | export default ({history}) => {
13 | return
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | }
--------------------------------------------------------------------------------
/packages/marketing/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
3 | const commonConfig = require('./webpack.common.js');
4 | const packageJson = require('../package.json');
5 |
6 | const prodConfig = {
7 | mode: 'production',
8 | output: {
9 | filename: '[name].[contenthash].js',
10 | publicPath: '/marketing/latest/'
11 | },
12 | plugins: [
13 | new ModuleFederationPlugin({
14 | name: 'marketing',
15 | filename: 'remoteEntry.js',
16 | exposes: {
17 | './MarketingApp': './src/bootstrap'
18 | },
19 | shared: packageJson.dependencies
20 | })
21 | ]
22 | }
23 |
24 | module.exports = merge(commonConfig, prodConfig)
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/packages/container/src/components/MarketingApp.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { mount } from 'marketing/MarketingApp';
3 | import { useHistory } from 'react-router-dom';
4 |
5 | export default () => {
6 | // get ref
7 | const ref = React.useRef(null);
8 | const history = useHistory();
9 |
10 | // use effect
11 | React.useEffect(() => {
12 | const { onParentNavigate } = mount(ref.current, {
13 | onNavigate: ({pathname: nextPathname}) => {
14 | const {pathname} = history.location;
15 |
16 | if( pathname !== nextPathname ) {
17 | history.push(nextPathname);
18 | }
19 | }
20 | })
21 |
22 | history.listen(onParentNavigate);
23 |
24 | return () => {
25 | history.listen(null);
26 | }
27 | }, []);
28 |
29 | return
;
30 | }
--------------------------------------------------------------------------------
/packages/container/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
3 | const commonConfig = require('./webpack.common.js');
4 | const packageJson = require('../package.json');
5 |
6 | const domain = process.env.PRODUCTION_DOMAIN;
7 |
8 | const prodConfig = {
9 | mode: 'production',
10 | output: {
11 | filename: '[name].[contenthash].js',
12 | publicPath: '/container/latest/'
13 | },
14 | plugins: [
15 | new ModuleFederationPlugin({
16 | name: 'container',
17 | remotes: {
18 | marketing: `marketing@${domain}/marketing/latest/remoteEntry.js`,
19 | },
20 | shared: packageJson.dependencies
21 | })
22 | ],
23 | }
24 |
25 | module.exports = merge(commonConfig, prodConfig);
--------------------------------------------------------------------------------
/packages/marketing/config/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const HTMLWebpackPlugin = require('html-webpack-plugin');
3 | const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
4 | const commonConfig = require('./webpack.common.js');
5 | const packageJson = require('../package.json');
6 |
7 | const devConfig = {
8 | mode: 'development',
9 | devServer: {
10 | port: 8081,
11 | historyApiFallback: {
12 | index: 'index.html'
13 | }
14 | },
15 | plugins: [
16 | new ModuleFederationPlugin({
17 | name: 'marketing',
18 | filename: 'remoteEntry.js',
19 | exposes: {
20 | './MarketingApp': './src/bootstrap'
21 | },
22 | shared: packageJson.dependencies
23 | }),
24 | new HTMLWebpackPlugin({
25 | template: './public/index.html',
26 | })
27 | ]
28 | }
29 |
30 | module.exports = merge(commonConfig, devConfig);
--------------------------------------------------------------------------------
/packages/auth/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auth",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "test": "echo \"Error: no test specified\" && exit 1"
6 | },
7 | "dependencies": {
8 | "@material-ui/core": "^4.11.0",
9 | "@material-ui/icons": "^4.9.1",
10 | "react": "^17.0.1",
11 | "react-dom": "^17.0.1",
12 | "react-router-dom": "^5.2.0"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "^7.12.3",
16 | "@babel/plugin-transform-runtime": "^7.12.1",
17 | "@babel/preset-env": "^7.12.1",
18 | "@babel/preset-react": "^7.12.1",
19 | "babel-loader": "^8.1.0",
20 | "clean-webpack-plugin": "^3.0.0",
21 | "css-loader": "^5.0.0",
22 | "html-webpack-plugin": "^4.5.0",
23 | "style-loader": "^2.0.0",
24 | "webpack": "^5.4.0",
25 | "webpack-cli": "^4.1.0",
26 | "webpack-dev-server": "^3.11.0",
27 | "webpack-merge": "^5.2.0"
28 | }
29 | }
--------------------------------------------------------------------------------
/packages/container/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "container",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "start": "webpack serve --config config/webpack.dev.js",
6 | "build": "webpack --config config/webpack.prod.js"
7 | },
8 | "dependencies": {
9 | "@material-ui/core": "^4.11.0",
10 | "@material-ui/icons": "^4.9.1",
11 | "react": "^17.0.1",
12 | "react-dom": "^17.0.1",
13 | "react-router-dom": "^5.2.0"
14 | },
15 | "devDependencies": {
16 | "@babel/core": "^7.12.3",
17 | "@babel/plugin-transform-runtime": "^7.12.1",
18 | "@babel/preset-env": "^7.12.1",
19 | "@babel/preset-react": "^7.12.1",
20 | "babel-loader": "^8.1.0",
21 | "clean-webpack-plugin": "^3.0.0",
22 | "css-loader": "^5.0.0",
23 | "html-webpack-plugin": "^4.5.0",
24 | "style-loader": "^2.0.0",
25 | "webpack": "^5.4.0",
26 | "webpack-cli": "^4.1.0",
27 | "webpack-dev-server": "^3.11.0",
28 | "webpack-merge": "^5.2.0"
29 | }
30 | }
--------------------------------------------------------------------------------
/packages/marketing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "marketing",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "start": "webpack serve --config config/webpack.dev.js",
6 | "build": "webpack --config config/webpack.prod.js"
7 | },
8 | "dependencies": {
9 | "@material-ui/core": "^4.11.0",
10 | "@material-ui/icons": "^4.9.1",
11 | "react": "^17.0.1",
12 | "react-dom": "^17.0.1",
13 | "react-router-dom": "^5.2.0"
14 | },
15 | "devDependencies": {
16 | "@babel/core": "^7.12.3",
17 | "@babel/plugin-transform-runtime": "^7.12.1",
18 | "@babel/preset-env": "^7.12.1",
19 | "@babel/preset-react": "^7.12.1",
20 | "babel-loader": "^8.1.0",
21 | "clean-webpack-plugin": "^3.0.0",
22 | "css-loader": "^5.0.0",
23 | "html-webpack-plugin": "^4.5.0",
24 | "style-loader": "^2.0.0",
25 | "webpack": "^5.4.0",
26 | "webpack-cli": "^4.1.0",
27 | "webpack-dev-server": "^3.11.0",
28 | "webpack-merge": "^5.2.0"
29 | }
30 | }
--------------------------------------------------------------------------------
/packages/dashboard/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dashboard",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "test": "echo \"Error: no test specified\" && exit 1"
6 | },
7 | "dependencies": {
8 | "chart.js": "^2.9.4",
9 | "primeflex": "^2.0.0",
10 | "primeicons": "^4.0.0",
11 | "primevue": "3.0.1",
12 | "vue": "^3.0.0"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "^7.12.3",
16 | "@babel/plugin-transform-runtime": "^7.12.1",
17 | "@babel/preset-env": "^7.12.1",
18 | "@vue/compiler-sfc": "^3.0.2",
19 | "babel-loader": "^8.1.0",
20 | "css-loader": "^5.0.0",
21 | "file-loader": "^6.2.0",
22 | "html-webpack-plugin": "^4.5.0",
23 | "node-sass": "^4.14.1",
24 | "sass-loader": "^10.0.4",
25 | "style-loader": "^2.0.0",
26 | "vue-loader": "^16.0.0-beta.9",
27 | "vue-style-loader": "^4.1.2",
28 | "webpack": "^5.4.0",
29 | "webpack-cli": "^4.1.0",
30 | "webpack-dev-server": "^3.11.0",
31 | "webpack-merge": "^5.2.0"
32 | }
33 | }
--------------------------------------------------------------------------------
/packages/marketing/src/bootstrap.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { createMemoryHistory, createBrowserHistory } from 'history'
4 | import App from './App';
5 |
6 | // Mount Function
7 | const mount = (el, { onNavigate, defaultHistory }) => {
8 | const history = defaultHistory || createMemoryHistory();
9 |
10 | if( onNavigate ) {
11 | history.listen(onNavigate)
12 | }
13 |
14 | ReactDOM.render(
15 | ,
16 | el
17 | );
18 |
19 | return {
20 | onParentNavigate({pathname: nextPathname}) {
21 | console.log('Containber just navigated')
22 | const { pathname } = history.location;
23 | if( pathname !== nextPathname ) {
24 | history.push(nextPathname);
25 | }
26 | }
27 | }
28 | }
29 |
30 |
31 | // if we are in development and in isolation,
32 | // call mount immediately
33 | if (process.env.NODE_ENV === 'development') {
34 | const devRoot = document.querySelector('#_marketing-dev-root');
35 |
36 | if (devRoot) {
37 | mount(devRoot, {defaultHistory: createBrowserHistory()});
38 | }
39 | }
40 |
41 | export { mount };
--------------------------------------------------------------------------------
/.github/workflows/marketing.yml:
--------------------------------------------------------------------------------
1 | name: deploy-marketing
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - 'packages/marketing/**'
9 | defaults:
10 | run:
11 | working-directory: packages/marketing
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v2
18 | - run: npm install
19 | - run: npm run build
20 |
21 | - uses: shinyinc/action-aws-cli@v1.2
22 | - run: aws s3 sync dist s3://${{ secrets.AWS_S3_BUCKET_NAME }}/marketing/latest
23 | env:
24 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
25 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
26 | AWS_DEFAULT_REGION: ${{ secrets.AWS_S3_BUCKET_REGION }}
27 | - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --paths "/marketing/latest/remoteEntry.js"
28 | env:
29 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
30 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
31 | AWS_DEFAULT_REGION: ${{ secrets.AWS_S3_BUCKET_REGION }}
--------------------------------------------------------------------------------
/.github/workflows/container.yml:
--------------------------------------------------------------------------------
1 | name: deploy-container
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - 'packages/container/**'
9 | defaults:
10 | run:
11 | working-directory: packages/container
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v2
18 | - run: npm install
19 | - run: npm run build
20 | env:
21 | PRODUCTION_DOMAIN: ${{ secrets.PRODUCTION_DOMAIN }}
22 | - uses: shinyinc/action-aws-cli@v1.2
23 | - run: aws s3 sync dist s3://${{ secrets.AWS_S3_BUCKET_NAME }}/container/latest
24 | env:
25 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
26 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
27 | AWS_DEFAULT_REGION: ${{ secrets.AWS_S3_BUCKET_REGION }}
28 | - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --paths "/container/latest/index.html"
29 | env:
30 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
31 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
32 | AWS_DEFAULT_REGION: ${{ secrets.AWS_S3_BUCKET_REGION }}
--------------------------------------------------------------------------------
/packages/container/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import AppBar from '@material-ui/core/AppBar';
3 | import Button from '@material-ui/core/Button';
4 | import Toolbar from '@material-ui/core/Toolbar';
5 | import Typography from '@material-ui/core/Typography';
6 | import { makeStyles } from '@material-ui/core/styles';
7 | import { Link as RouterLink } from 'react-router-dom';
8 |
9 | const useStyles = makeStyles((theme) => ({
10 | '@global': {
11 | ul: {
12 | margin: 0,
13 | padding: 0,
14 | listStyle: 'none',
15 | },
16 | a: {
17 | textDecoration: 'none',
18 | },
19 | },
20 | appBar: {
21 | borderBottom: `1px solid ${theme.palette.divider}`,
22 | },
23 | toolbar: {
24 | flexWrap: 'wrap',
25 | justifyContent: 'space-between',
26 | },
27 | link: {
28 | margin: theme.spacing(1, 1.5),
29 | },
30 | heroContent: {
31 | padding: theme.spacing(8, 0, 6),
32 | },
33 | cardHeader: {
34 | backgroundColor:
35 | theme.palette.type === 'light'
36 | ? theme.palette.grey[200]
37 | : theme.palette.grey[700],
38 | },
39 | cardPricing: {
40 | display: 'flex',
41 | justifyContent: 'center',
42 | alignItems: 'baseline',
43 | marginBottom: theme.spacing(2),
44 | },
45 | footer: {
46 | borderTop: `1px solid ${theme.palette.divider}`,
47 | marginTop: theme.spacing(8),
48 | paddingTop: theme.spacing(3),
49 | paddingBottom: theme.spacing(3),
50 | [theme.breakpoints.up('sm')]: {
51 | paddingTop: theme.spacing(6),
52 | paddingBottom: theme.spacing(6),
53 | },
54 | },
55 | }));
56 |
57 | export default function Header({ signedIn, onSignOut }) {
58 | const classes = useStyles();
59 |
60 | const onClick = () => {
61 | if (signedIn && onSignOut) {
62 | onSignOut();
63 | }
64 | };
65 |
66 | return (
67 |
68 |
74 |
75 |
82 | App
83 |
84 |
92 | {signedIn ? 'Logout' : 'Login'}
93 |
94 |
95 |
96 |
97 | );
98 | }
99 |
--------------------------------------------------------------------------------
/packages/marketing/src/components/Landing.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@material-ui/core/Button';
3 | import Card from '@material-ui/core/Card';
4 | import CardActions from '@material-ui/core/CardActions';
5 | import CardContent from '@material-ui/core/CardContent';
6 | import CardMedia from '@material-ui/core/CardMedia';
7 | import Grid from '@material-ui/core/Grid';
8 | import Typography from '@material-ui/core/Typography';
9 | import { makeStyles } from '@material-ui/core/styles';
10 | import Container from '@material-ui/core/Container';
11 | import MaterialLink from '@material-ui/core/Link';
12 | import { Link } from 'react-router-dom';
13 |
14 | function Copyright() {
15 | return (
16 |
17 | {'Copyright © '}
18 |
19 | Your Website
20 | {' '}
21 | {new Date().getFullYear()}
22 | {'.'}
23 |
24 | );
25 | }
26 |
27 | const useStyles = makeStyles((theme) => ({
28 | '@global': {
29 | a: {
30 | textDecoration: 'none',
31 | },
32 | },
33 | icon: {
34 | marginRight: theme.spacing(2),
35 | },
36 | heroContent: {
37 | backgroundColor: theme.palette.background.paper,
38 | padding: theme.spacing(8, 0, 6),
39 | },
40 | heroButtons: {
41 | marginTop: theme.spacing(4),
42 | },
43 | cardGrid: {
44 | paddingTop: theme.spacing(8),
45 | paddingBottom: theme.spacing(8),
46 | },
47 | card: {
48 | height: '100%',
49 | display: 'flex',
50 | flexDirection: 'column',
51 | },
52 | cardMedia: {
53 | paddingTop: '56.25%', // 16:9
54 | },
55 | cardContent: {
56 | flexGrow: 1,
57 | },
58 | footer: {
59 | backgroundColor: theme.palette.background.paper,
60 | padding: theme.spacing(6),
61 | },
62 | }));
63 |
64 | const cards = [1, 2, 3, 4, 5, 6, 7, 8, 9];
65 |
66 | export default function Album() {
67 | const classes = useStyles();
68 |
69 | return (
70 |
71 |
72 | {/* Hero unit */}
73 |
74 |
75 |
82 | Home Page
83 |
84 |
90 | Something short and leading about the collection below—its
91 | contents, the creator, etc. Make it short and sweet, but not too
92 | short so folks don't simply skip over it entirely.
93 |
94 |
95 |
96 |
97 |
98 |
99 | Pricing
100 |
101 |
102 |
103 |
104 |
105 |
106 | Pricing
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | {/* End hero unit */}
116 |
117 | {cards.map((card) => (
118 |
119 |
120 |
125 |
126 |
127 | Heading
128 |
129 |
130 | This is a media card. You can use this section to describe
131 | the content.
132 |
133 |
134 |
135 |
136 | View
137 |
138 |
139 | Edit
140 |
141 |
142 |
143 |
144 | ))}
145 |
146 |
147 |
148 | {/* Footer */}
149 |
150 |
151 | Footer
152 |
153 |
159 | Something here to give the footer a purpose!
160 |
161 |
162 |
163 | {/* End footer */}
164 |
165 | );
166 | }
167 |
--------------------------------------------------------------------------------
/packages/marketing/src/components/Pricing.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@material-ui/core/Button';
3 | import Card from '@material-ui/core/Card';
4 | import CardActions from '@material-ui/core/CardActions';
5 | import CardContent from '@material-ui/core/CardContent';
6 | import CardHeader from '@material-ui/core/CardHeader';
7 | import Grid from '@material-ui/core/Grid';
8 | import StarIcon from '@material-ui/icons/StarBorder';
9 | import Typography from '@material-ui/core/Typography';
10 | import Link from '@material-ui/core/Link';
11 | import { makeStyles } from '@material-ui/core/styles';
12 | import Container from '@material-ui/core/Container';
13 | import Box from '@material-ui/core/Box';
14 | import { Link as RouterLink } from 'react-router-dom';
15 |
16 | function Copyright() {
17 | return (
18 |
19 | {'Copyright © '}
20 |
21 | Your Website
22 | {' '}
23 | {new Date().getFullYear()}
24 | {'.'}
25 |
26 | );
27 | }
28 |
29 | const useStyles = makeStyles((theme) => ({
30 | '@global': {
31 | ul: {
32 | margin: 0,
33 | padding: 0,
34 | listStyle: 'none',
35 | },
36 | },
37 | toolbar: {
38 | flexWrap: 'wrap',
39 | },
40 | toolbarTitle: {
41 | flexGrow: 1,
42 | },
43 | link: {
44 | margin: theme.spacing(1, 1.5),
45 | },
46 | heroContent: {
47 | padding: theme.spacing(8, 0, 6),
48 | },
49 | cardHeader: {
50 | backgroundColor:
51 | theme.palette.type === 'light'
52 | ? theme.palette.grey[200]
53 | : theme.palette.grey[700],
54 | },
55 | cardPricing: {
56 | display: 'flex',
57 | justifyContent: 'center',
58 | alignItems: 'baseline',
59 | marginBottom: theme.spacing(2),
60 | },
61 | footer: {
62 | borderTop: `1px solid ${theme.palette.divider}`,
63 | marginTop: theme.spacing(8),
64 | paddingTop: theme.spacing(3),
65 | paddingBottom: theme.spacing(3),
66 | [theme.breakpoints.up('sm')]: {
67 | paddingTop: theme.spacing(6),
68 | paddingBottom: theme.spacing(6),
69 | },
70 | },
71 | }));
72 |
73 | const tiers = [
74 | {
75 | title: 'Free',
76 | price: '0',
77 | description: [
78 | '10 users included',
79 | '2 GB of storage',
80 | 'Help center access',
81 | 'Email support',
82 | ],
83 | buttonText: 'Sign up for free',
84 | buttonVariant: 'outlined',
85 | },
86 | {
87 | title: 'Pro',
88 | subheader: 'Most popular',
89 | price: '15',
90 | description: [
91 | '20 users included',
92 | '10 GB of storage',
93 | 'Help center access',
94 | 'Priority email support',
95 | ],
96 | buttonText: 'Get started',
97 | buttonVariant: 'contained',
98 | },
99 | {
100 | title: 'Enterprise',
101 | price: '30',
102 | description: [
103 | '50 users included',
104 | '30 GB of storage',
105 | 'Help center access',
106 | 'Phone & email support',
107 | ],
108 | buttonText: 'Contact us',
109 | buttonVariant: 'outlined',
110 | },
111 | ];
112 | const footers = [
113 | {
114 | title: 'Company',
115 | description: ['Team', 'History', 'Contact us', 'Locations'],
116 | },
117 | {
118 | title: 'Features',
119 | description: [
120 | 'Cool stuff',
121 | 'Random feature',
122 | 'Team feature',
123 | 'Developer stuff',
124 | 'Another one',
125 | ],
126 | },
127 | {
128 | title: 'Resources',
129 | description: [
130 | 'Resource',
131 | 'Resource name',
132 | 'Another resource',
133 | 'Final resource',
134 | ],
135 | },
136 | {
137 | title: 'Legal',
138 | description: ['Privacy policy', 'Terms of use'],
139 | },
140 | ];
141 |
142 | export default function Pricing() {
143 | const classes = useStyles();
144 |
145 | return (
146 |
147 | {/* Hero unit */}
148 |
149 |
156 | Pricing
157 |
158 |
164 | Quickly build an effective pricing table for your potential customers
165 | with this layout. It's built with default Material-UI components
166 | with little customization.
167 |
168 |
169 | {/* End hero unit */}
170 |
171 |
172 | {tiers.map((tier) => (
173 | // Enterprise card is full width at sm breakpoint
174 |
181 |
182 | : null}
188 | className={classes.cardHeader}
189 | />
190 |
191 |
192 |
193 | ${tier.price}
194 |
195 |
196 | /mo
197 |
198 |
199 |
200 | {tier.description.map((line) => (
201 |
207 | {line}
208 |
209 | ))}
210 |
211 |
212 |
213 |
219 | {tier.buttonText}
220 |
221 |
222 |
223 |
224 | ))}
225 |
226 |
227 | {/* Footer */}
228 |
229 |
230 | {footers.map((footer) => (
231 |
232 |
233 | {footer.title}
234 |
235 |
236 | {footer.description.map((item) => (
237 |
238 |
239 | {item}
240 |
241 |
242 | ))}
243 |
244 |
245 | ))}
246 |
247 |
248 |
249 |
250 |
251 | {/* End footer */}
252 |
253 | );
254 | }
255 |
--------------------------------------------------------------------------------