├── .gitignore
├── packages
├── auth
│ ├── src
│ │ ├── index.js
│ │ ├── App.js
│ │ ├── bootstrap.js
│ │ └── components
│ │ │ ├── Signin.js
│ │ │ └── Signup.js
│ ├── public
│ │ └── index.html
│ ├── config
│ │ ├── webpack.common.js
│ │ ├── webpack.prod.js
│ │ └── webpack.dev.js
│ └── package.json
├── container
│ ├── src
│ │ ├── index.js
│ │ ├── bootstrap.js
│ │ ├── components
│ │ │ ├── DashboardApp.js
│ │ │ ├── Progress.js
│ │ │ ├── MarketingApp.js
│ │ │ ├── AuthApp.js
│ │ │ └── Header.js
│ │ └── App.js
│ ├── public
│ │ └── index.html
│ ├── config
│ │ ├── webpack.common.js
│ │ ├── webpack.prod.js
│ │ └── webpack.dev.js
│ └── package.json
├── dashboard
│ ├── src
│ │ ├── index.js
│ │ ├── bootstrap.js
│ │ └── components
│ │ │ └── Dashboard.vue
│ ├── public
│ │ └── index.html
│ ├── config
│ │ ├── webpack.prod.js
│ │ ├── webpack.common.js
│ │ └── webpack.dev.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
└── .github
└── workflows
├── auth.yml
├── dashboard.yml
├── marketing.yml
└── container.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .DS_Store
--------------------------------------------------------------------------------
/packages/auth/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
2 |
--------------------------------------------------------------------------------
/packages/container/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
2 |
--------------------------------------------------------------------------------
/packages/dashboard/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
2 |
--------------------------------------------------------------------------------
/packages/marketing/src/index.js:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
2 |
--------------------------------------------------------------------------------
/packages/container/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/auth/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/dashboard/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/marketing/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/container/src/bootstrap.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render( , document.querySelector('#root'));
6 |
--------------------------------------------------------------------------------
/packages/container/src/components/DashboardApp.js:
--------------------------------------------------------------------------------
1 | import { mount } from 'dashboard/DashboardApp';
2 | import React, { useRef, useEffect } from 'react';
3 |
4 | export default () => {
5 | const ref = useRef(null);
6 |
7 | useEffect(() => {
8 | mount(ref.current);
9 | }, []);
10 |
11 | return
;
12 | };
13 |
--------------------------------------------------------------------------------
/packages/auth/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 | };
18 |
--------------------------------------------------------------------------------
/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 | };
18 |
--------------------------------------------------------------------------------
/packages/container/src/components/Progress.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { makeStyles, createStyles } from '@material-ui/core/styles';
3 | import LinearProgress from '@material-ui/core/LinearProgress';
4 |
5 | const useStyles = makeStyles((theme) => {
6 | return createStyles({
7 | bar: {
8 | width: '100%',
9 | '& > * + *': {
10 | marginTop: theme.spacing(2),
11 | },
12 | },
13 | });
14 | });
15 |
16 | export default () => {
17 | const classes = useStyles();
18 |
19 | return (
20 |
21 |
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/packages/container/config/webpack.common.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 |
3 | module.exports = {
4 | module: {
5 | rules: [
6 | {
7 | test: /\.m?js$/,
8 | exclude: /node_modules/,
9 | use: {
10 | loader: 'babel-loader',
11 | options: {
12 | presets: ['@babel/preset-react', '@babel/preset-env'],
13 | plugins: ['@babel/plugin-transform-runtime'],
14 | },
15 | },
16 | },
17 | ],
18 | },
19 | plugins: [
20 | new HtmlWebpackPlugin({
21 | template: './public/index.html',
22 | }),
23 | ],
24 | };
25 |
--------------------------------------------------------------------------------
/packages/dashboard/src/bootstrap.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import Dashboard from './components/Dashboard.vue';
3 |
4 | // Mount function to start up the app
5 | const mount = (el) => {
6 | const app = createApp(Dashboard);
7 | app.mount(el);
8 | };
9 |
10 | // If we are in development and in isolation,
11 | // call mount immediately
12 | if (process.env.NODE_ENV === 'development') {
13 | const devRoot = document.querySelector('#_dashboard-dev-root');
14 |
15 | if (devRoot) {
16 | mount(devRoot);
17 | }
18 | }
19 |
20 | // We are running through container
21 | // and we should export the mount function
22 | export { mount };
23 |
--------------------------------------------------------------------------------
/packages/auth/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
3 | const packageJson = require('../package.json');
4 | const commonConfig = require('./webpack.common');
5 |
6 | const prodConfig = {
7 | mode: 'production',
8 | output: {
9 | filename: '[name].[contenthash].js',
10 | publicPath: '/auth/latest/',
11 | },
12 | plugins: [
13 | new ModuleFederationPlugin({
14 | name: 'auth',
15 | filename: 'remoteEntry.js',
16 | exposes: {
17 | './AuthApp': './src/bootstrap',
18 | },
19 | shared: packageJson.dependencies,
20 | }),
21 | ],
22 | };
23 |
24 | module.exports = merge(commonConfig, prodConfig);
25 |
--------------------------------------------------------------------------------
/packages/container/src/components/MarketingApp.js:
--------------------------------------------------------------------------------
1 | import { mount } from 'marketing/MarketingApp';
2 | import React, { useRef, useEffect } from 'react';
3 | import { useHistory } from 'react-router-dom';
4 |
5 | export default () => {
6 | const ref = useRef(null);
7 | const history = useHistory();
8 |
9 | useEffect(() => {
10 | const { onParentNavigate } = mount(ref.current, {
11 | initialPath: history.location.pathname,
12 | onNavigate: ({ pathname: nextPathname }) => {
13 | const { pathname } = history.location;
14 |
15 | if (pathname !== nextPathname) {
16 | history.push(nextPathname);
17 | }
18 | },
19 | });
20 |
21 | history.listen(onParentNavigate);
22 | }, []);
23 |
24 | return
;
25 | };
26 |
--------------------------------------------------------------------------------
/packages/dashboard/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
3 | const packageJson = require('../package.json');
4 | const commonConfig = require('./webpack.common');
5 |
6 | const prodConfig = {
7 | mode: 'production',
8 | output: {
9 | filename: '[name].[contenthash].js',
10 | publicPath: '/dashboard/latest/',
11 | },
12 | plugins: [
13 | new ModuleFederationPlugin({
14 | name: 'dashboard',
15 | filename: 'remoteEntry.js',
16 | exposes: {
17 | './DashboardApp': './src/bootstrap',
18 | },
19 | shared: packageJson.dependencies,
20 | }),
21 | ],
22 | };
23 |
24 | module.exports = merge(commonConfig, prodConfig);
25 |
--------------------------------------------------------------------------------
/packages/marketing/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
3 | const packageJson = require('../package.json');
4 | const commonConfig = require('./webpack.common');
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 |
--------------------------------------------------------------------------------
/packages/container/src/components/AuthApp.js:
--------------------------------------------------------------------------------
1 | import { mount } from 'auth/AuthApp';
2 | import React, { useRef, useEffect } from 'react';
3 | import { useHistory } from 'react-router-dom';
4 |
5 | export default ({ onSignIn }) => {
6 | const ref = useRef(null);
7 | const history = useHistory();
8 |
9 | useEffect(() => {
10 | const { onParentNavigate } = mount(ref.current, {
11 | initialPath: history.location.pathname,
12 | onNavigate: ({ pathname: nextPathname }) => {
13 | const { pathname } = history.location;
14 |
15 | if (pathname !== nextPathname) {
16 | history.push(nextPathname);
17 | }
18 | },
19 | onSignIn,
20 | });
21 |
22 | history.listen(onParentNavigate);
23 | }, []);
24 |
25 | return
;
26 | };
27 |
--------------------------------------------------------------------------------
/packages/marketing/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Switch, Route, Router } from 'react-router-dom';
3 | import {
4 | StylesProvider,
5 | createGenerateClassName,
6 | } from '@material-ui/core/styles';
7 |
8 | import Landing from './components/Landing';
9 | import Pricing from './components/Pricing';
10 |
11 | const generateClassName = createGenerateClassName({
12 | productionPrefix: 'ma',
13 | });
14 |
15 | export default ({ history }) => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/packages/auth/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auth",
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/auth/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Switch, Route, Router } from 'react-router-dom';
3 | import {
4 | StylesProvider,
5 | createGenerateClassName,
6 | } from '@material-ui/core/styles';
7 |
8 | import Signin from './components/Signin';
9 | import Signup from './components/Signup';
10 |
11 | const generateClassName = createGenerateClassName({
12 | productionPrefix: 'au',
13 | });
14 |
15 | export default ({ history, onSignIn }) => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | );
32 | };
33 |
--------------------------------------------------------------------------------
/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');
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 | auth: `auth@${domain}/auth/latest/remoteEntry.js`,
20 | dashboard: `dashboard@${domain}/dashboard/latest/remoteEntry.js`,
21 | },
22 | shared: packageJson.dependencies,
23 | }),
24 | ],
25 | };
26 |
27 | module.exports = merge(commonConfig, prodConfig);
28 |
--------------------------------------------------------------------------------
/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/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');
4 | const packageJson = require('../package.json');
5 |
6 | const devConfig = {
7 | mode: 'development',
8 | output: {
9 | publicPath: 'http://localhost:8080/',
10 | },
11 | devServer: {
12 | port: 8080,
13 | historyApiFallback: {
14 | index: 'index.html',
15 | },
16 | },
17 | plugins: [
18 | new ModuleFederationPlugin({
19 | name: 'container',
20 | remotes: {
21 | marketing: 'marketing@http://localhost:8081/remoteEntry.js',
22 | auth: 'auth@http://localhost:8082/remoteEntry.js',
23 | dashboard: 'dashboard@http://localhost:8083/remoteEntry.js',
24 | },
25 | shared: packageJson.dependencies,
26 | }),
27 | ],
28 | };
29 |
30 | module.exports = merge(commonConfig, devConfig);
31 |
--------------------------------------------------------------------------------
/packages/auth/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');
5 | const packageJson = require('../package.json');
6 |
7 | const devConfig = {
8 | mode: 'development',
9 | output: {
10 | publicPath: 'http://localhost:8082/',
11 | },
12 | devServer: {
13 | port: 8082,
14 | historyApiFallback: {
15 | index: 'index.html',
16 | },
17 | },
18 | plugins: [
19 | new ModuleFederationPlugin({
20 | name: 'auth',
21 | filename: 'remoteEntry.js',
22 | exposes: {
23 | './AuthApp': './src/bootstrap',
24 | },
25 | shared: packageJson.dependencies,
26 | }),
27 | new HtmlWebpackPlugin({
28 | template: './public/index.html',
29 | }),
30 | ],
31 | };
32 |
33 | module.exports = merge(commonConfig, devConfig);
34 |
--------------------------------------------------------------------------------
/.github/workflows/auth.yml:
--------------------------------------------------------------------------------
1 | name: deploy-auth
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'packages/auth/**'
9 |
10 | defaults:
11 | run:
12 | working-directory: packages/auth
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - run: npm install
21 | - run: npm run build
22 |
23 | - uses: chrislennon/action-aws-cli@v1.1
24 | - run: aws s3 sync dist s3://${{ secrets.AWS_S3_BUCKET_NAME }}/auth/latest
25 | env:
26 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
27 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
28 |
29 | - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_DISTRIBUTION_ID }} --paths "/auth/latest/remoteEntry.js"
30 | env:
31 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
32 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
33 |
--------------------------------------------------------------------------------
/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');
5 | const packageJson = require('../package.json');
6 |
7 | const devConfig = {
8 | mode: 'development',
9 | output: {
10 | publicPath: 'http://localhost:8081/',
11 | },
12 | devServer: {
13 | port: 8081,
14 | historyApiFallback: {
15 | index: 'index.html',
16 | },
17 | },
18 | plugins: [
19 | new ModuleFederationPlugin({
20 | name: 'marketing',
21 | filename: 'remoteEntry.js',
22 | exposes: {
23 | './MarketingApp': './src/bootstrap',
24 | },
25 | shared: packageJson.dependencies,
26 | }),
27 | new HtmlWebpackPlugin({
28 | template: './public/index.html',
29 | }),
30 | ],
31 | };
32 |
33 | module.exports = merge(commonConfig, devConfig);
34 |
--------------------------------------------------------------------------------
/.github/workflows/dashboard.yml:
--------------------------------------------------------------------------------
1 | name: deploy-dashboard
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'packages/dashboard/**'
9 |
10 | defaults:
11 | run:
12 | working-directory: packages/dashboard
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - run: npm install
21 | - run: npm run build
22 |
23 | - uses: chrislennon/action-aws-cli@v1.1
24 | - run: aws s3 sync dist s3://${{ secrets.AWS_S3_BUCKET_NAME }}/dashboard/latest
25 | env:
26 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
27 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
28 |
29 | - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_DISTRIBUTION_ID }} --paths "/dashboard/latest/remoteEntry.js"
30 | env:
31 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
32 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
33 |
--------------------------------------------------------------------------------
/.github/workflows/marketing.yml:
--------------------------------------------------------------------------------
1 | name: deploy-marketing
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'packages/marketing/**'
9 |
10 | defaults:
11 | run:
12 | working-directory: packages/marketing
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - run: npm install
21 | - run: npm run build
22 |
23 | - uses: chrislennon/action-aws-cli@v1.1
24 | - run: aws s3 sync dist s3://${{ secrets.AWS_S3_BUCKET_NAME }}/marketing/latest
25 | env:
26 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
27 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
28 |
29 | - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_DISTRIBUTION_ID }} --paths "/marketing/latest/remoteEntry.js"
30 | env:
31 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
32 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
33 |
--------------------------------------------------------------------------------
/packages/dashboard/config/webpack.common.js:
--------------------------------------------------------------------------------
1 | const { VueLoaderPlugin } = require('vue-loader');
2 |
3 | module.exports = {
4 | entry: './src/index.js',
5 | output: {
6 | filename: '[name].[contenthash].js',
7 | },
8 | resolve: {
9 | extensions: ['.js', '.vue'],
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.(png|jpe?g|gif|woff|svg|eot|ttf)$/i,
15 | use: [{ loader: 'file-loader' }],
16 | },
17 | {
18 | test: /\.vue$/,
19 | use: 'vue-loader',
20 | },
21 | {
22 | test: /\.scss|\.css$/,
23 | use: ['vue-style-loader', 'style-loader', 'css-loader', 'sass-loader'],
24 | },
25 | {
26 | test: /\.m?js$/,
27 | exclude: /node_modules/,
28 | use: {
29 | loader: 'babel-loader',
30 | options: {
31 | presets: ['@babel/preset-env'],
32 | plugins: ['@babel/plugin-transform-runtime'],
33 | },
34 | },
35 | },
36 | ],
37 | },
38 | plugins: [new VueLoaderPlugin()],
39 | };
40 |
--------------------------------------------------------------------------------
/packages/dashboard/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dashboard",
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 | "chart.js": "^2.9.4",
10 | "primeflex": "^2.0.0",
11 | "primeicons": "^4.0.0",
12 | "primevue": "^3.0.1",
13 | "vue": "^3.0.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 | "@vue/compiler-sfc": "^3.0.2",
20 | "babel-loader": "^8.1.0",
21 | "css-loader": "^5.0.0",
22 | "file-loader": "^6.2.0",
23 | "html-webpack-plugin": "^4.5.0",
24 | "node-sass": "^4.14.1",
25 | "sass-loader": "^10.0.4",
26 | "style-loader": "^2.0.0",
27 | "vue-loader": "^16.0.0-beta.9",
28 | "vue-style-loader": "^4.1.2",
29 | "webpack": "^5.4.0",
30 | "webpack-cli": "^4.1.0",
31 | "webpack-dev-server": "^3.11.0",
32 | "webpack-merge": "^5.2.0"
33 | }
34 | }
--------------------------------------------------------------------------------
/packages/dashboard/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');
5 | const packageJson = require('../package.json');
6 |
7 | const devConfig = {
8 | mode: 'development',
9 | output: {
10 | publicPath: 'http://localhost:8083/',
11 | },
12 | devServer: {
13 | port: 8083,
14 | historyApiFallback: {
15 | index: 'index.html',
16 | },
17 | headers: {
18 | 'Access-Control-Allow-Origin': '*',
19 | },
20 | },
21 | plugins: [
22 | new ModuleFederationPlugin({
23 | name: 'dashboard',
24 | filename: 'remoteEntry.js',
25 | exposes: {
26 | './DashboardApp': './src/bootstrap',
27 | },
28 | shared: packageJson.dependencies,
29 | }),
30 | new HtmlWebpackPlugin({
31 | template: './public/index.html',
32 | }),
33 | ],
34 | };
35 |
36 | module.exports = merge(commonConfig, devConfig);
37 |
--------------------------------------------------------------------------------
/.github/workflows/container.yml:
--------------------------------------------------------------------------------
1 | name: deploy-container
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths:
8 | - 'packages/container/**'
9 |
10 | defaults:
11 | run:
12 | working-directory: packages/container
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - run: npm install
21 | - run: npm run build
22 | env:
23 | PRODUCTION_DOMAIN: ${{ secrets.PRODUCTION_DOMAIN }}
24 |
25 | - uses: shinyinc/action-aws-cli@v1.2
26 | - run: aws s3 sync dist s3://${{ secrets.AWS_S3_BUCKET_NAME }}/container/latest
27 | env:
28 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
29 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
30 |
31 | - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_DISTRIBUTION_ID }} --paths "/container/latest/index.html"
32 | env:
33 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
34 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
35 |
--------------------------------------------------------------------------------
/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 to start up the app
7 | const mount = (el, { onNavigate, defaultHistory, initialPath }) => {
8 | const history =
9 | defaultHistory ||
10 | createMemoryHistory({
11 | initialEntries: [initialPath],
12 | });
13 |
14 | if (onNavigate) {
15 | history.listen(onNavigate);
16 | }
17 |
18 | ReactDOM.render( , el);
19 |
20 | return {
21 | onParentNavigate({ pathname: nextPathname }) {
22 | const { pathname } = history.location;
23 |
24 | if (pathname !== nextPathname) {
25 | history.push(nextPathname);
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 | // We are running through container
42 | // and we should export the mount function
43 | export { mount };
44 |
--------------------------------------------------------------------------------
/packages/auth/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 to start up the app
7 | const mount = (el, { onSignIn, onNavigate, defaultHistory, initialPath }) => {
8 | const history =
9 | defaultHistory ||
10 | createMemoryHistory({
11 | initialEntries: [initialPath],
12 | });
13 |
14 | if (onNavigate) {
15 | history.listen(onNavigate);
16 | }
17 |
18 | ReactDOM.render( , el);
19 |
20 | return {
21 | onParentNavigate({ pathname: nextPathname }) {
22 | const { pathname } = history.location;
23 |
24 | if (pathname !== nextPathname) {
25 | history.push(nextPathname);
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('#_auth-dev-root');
35 |
36 | if (devRoot) {
37 | mount(devRoot, { defaultHistory: createBrowserHistory() });
38 | }
39 | }
40 |
41 | // We are running through container
42 | // and we should export the mount function
43 | export { mount };
44 |
--------------------------------------------------------------------------------
/packages/container/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { lazy, Suspense, useState, useEffect } from 'react';
2 | import { Router, Route, Switch, Redirect } from 'react-router-dom';
3 | import {
4 | StylesProvider,
5 | createGenerateClassName,
6 | } from '@material-ui/core/styles';
7 | import { createBrowserHistory } from 'history';
8 |
9 | import Progress from './components/Progress';
10 | import Header from './components/Header';
11 |
12 | const MarketingLazy = lazy(() => import('./components/MarketingApp'));
13 | const AuthLazy = lazy(() => import('./components/AuthApp'));
14 | const DashboardLazy = lazy(() => import('./components/DashboardApp'));
15 |
16 | const generateClassName = createGenerateClassName({
17 | productionPrefix: 'co',
18 | });
19 |
20 | const history = createBrowserHistory();
21 |
22 | export default () => {
23 | const [isSignedIn, setIsSignedIn] = useState(false);
24 |
25 | useEffect(() => {
26 | if (isSignedIn) {
27 | history.push('/dashboard');
28 | }
29 | }, [isSignedIn]);
30 |
31 | return (
32 |
33 |
34 |
35 |
setIsSignedIn(false)}
37 | isSignedIn={isSignedIn}
38 | />
39 | }>
40 |
41 |
42 | setIsSignedIn(true)} />
43 |
44 |
45 | {!isSignedIn && }
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | };
56 |
--------------------------------------------------------------------------------
/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({ isSignedIn, onSignOut }) {
58 | const classes = useStyles();
59 |
60 | const onClick = () => {
61 | if (isSignedIn && onSignOut) {
62 | onSignOut();
63 | }
64 | };
65 |
66 | return (
67 |
68 |
74 |
75 |
82 | App
83 |
84 |
92 | {isSignedIn ? 'Logout' : 'Login'}
93 |
94 |
95 |
96 |
97 | );
98 | }
99 |
--------------------------------------------------------------------------------
/packages/auth/src/components/Signin.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Avatar from '@material-ui/core/Avatar';
3 | import Button from '@material-ui/core/Button';
4 | import TextField from '@material-ui/core/TextField';
5 | import FormControlLabel from '@material-ui/core/FormControlLabel';
6 | import Checkbox from '@material-ui/core/Checkbox';
7 | import Grid from '@material-ui/core/Grid';
8 | import Box from '@material-ui/core/Box';
9 | import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
10 | import Typography from '@material-ui/core/Typography';
11 | import { makeStyles } from '@material-ui/core/styles';
12 | import Container from '@material-ui/core/Container';
13 | import { Link } from 'react-router-dom';
14 |
15 | function Copyright() {
16 | return (
17 |
18 | {'Copyright © '}
19 |
20 | Your Website
21 | {' '}
22 | {new Date().getFullYear()}
23 | {'.'}
24 |
25 | );
26 | }
27 |
28 | const useStyles = makeStyles((theme) => ({
29 | '@global': {
30 | a: {
31 | textDecoration: 'none',
32 | },
33 | },
34 | paper: {
35 | marginTop: theme.spacing(8),
36 | display: 'flex',
37 | flexDirection: 'column',
38 | alignItems: 'center',
39 | },
40 | avatar: {
41 | margin: theme.spacing(1),
42 | backgroundColor: theme.palette.secondary.main,
43 | },
44 | form: {
45 | width: '100%',
46 | marginTop: theme.spacing(1),
47 | },
48 | submit: {
49 | margin: theme.spacing(3, 0, 2),
50 | },
51 | }));
52 |
53 | export default function SignIn({ onSignIn }) {
54 | const classes = useStyles();
55 |
56 | return (
57 |
58 |
59 |
60 |
61 |
62 |
63 | Sign in
64 |
65 |
112 |
113 |
114 |
115 |
116 |
117 | );
118 | }
119 |
--------------------------------------------------------------------------------
/packages/auth/src/components/Signup.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Avatar from '@material-ui/core/Avatar';
3 | import Button from '@material-ui/core/Button';
4 | import TextField from '@material-ui/core/TextField';
5 | import FormControlLabel from '@material-ui/core/FormControlLabel';
6 | import Checkbox from '@material-ui/core/Checkbox';
7 | import Grid from '@material-ui/core/Grid';
8 | import Box from '@material-ui/core/Box';
9 | import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
10 | import Typography from '@material-ui/core/Typography';
11 | import { makeStyles } from '@material-ui/core/styles';
12 | import Container from '@material-ui/core/Container';
13 | import { Link } from 'react-router-dom';
14 |
15 | function Copyright() {
16 | return (
17 |
18 | {'Copyright © '}
19 | Your Website {new Date().getFullYear()}
20 | {'.'}
21 |
22 | );
23 | }
24 |
25 | const useStyles = makeStyles((theme) => ({
26 | '@global': {
27 | a: {
28 | textDecoration: 'none',
29 | },
30 | },
31 | paper: {
32 | marginTop: theme.spacing(8),
33 | display: 'flex',
34 | flexDirection: 'column',
35 | alignItems: 'center',
36 | },
37 | avatar: {
38 | margin: theme.spacing(1),
39 | backgroundColor: theme.palette.secondary.main,
40 | },
41 | form: {
42 | width: '100%',
43 | marginTop: theme.spacing(3),
44 | },
45 | submit: {
46 | margin: theme.spacing(3, 0, 2),
47 | },
48 | }));
49 |
50 | export default function SignUp({ onSignIn }) {
51 | const classes = useStyles();
52 |
53 | return (
54 |
55 |
56 |
57 |
58 |
59 |
60 | Sign up
61 |
62 |
137 |
138 |
139 |
140 |
141 |
142 | );
143 | }
144 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/packages/dashboard/src/components/Dashboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Users
6 | Number of visitors
7 | 12
8 |
9 |
10 |
11 |
12 | Sales
13 | Number of purchases
14 | 534
15 |
16 |
17 |
18 |
19 | Revenue
20 | Income for today
21 | $3,200
22 |
23 |
24 |
25 |
26 |
27 |
28 | TV
29 |
30 |
31 |
32 | Total Queries
33 | 523
34 |
35 |
36 |
37 |
38 |
39 |
40 | TI
41 |
42 |
43 |
44 | Total Issues
45 | 81
46 |
47 |
48 |
49 |
50 |
51 |
52 | OI
53 |
54 |
55 |
56 | Open Issues
57 | 21
58 |
59 |
60 |
61 |
62 |
63 |
64 | CI
65 |
66 |
67 |
68 | Closed Issues
69 | 60
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | Sales Reports
80 |
81 |
82 |
83 |
84 | Pay Invoices
85 |
86 |
87 |
88 |
89 | Dinner with Tony
90 |
91 |
92 |
93 |
94 | Client Meeting
95 |
96 |
97 |
98 |
99 | New Theme
100 |
101 |
102 |
103 |
104 | Flight Ticket
105 |
106 |
107 |
108 |
109 |
110 |
111 |
137 |
138 |
168 |
169 |
170 |
171 |
182 |
183 |
184 |
185 |
186 |
Income
187 |
$900
188 |
189 |
190 |
191 |
192 |
193 |
Tax
194 |
$250
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
Recent Sales
205 |
212 |
213 | Logo
214 |
215 |
220 |
221 |
222 |
223 |
224 |
225 |
226 | {{ formatCurrency(slotProps.data.price) }}
227 |
228 |
229 |
230 | View
231 |
232 |
237 |
242 |
243 |
244 |
245 |
246 |
247 |
252 |
253 |
254 |
255 |
345 |
346 |
753 |
--------------------------------------------------------------------------------