├── conf
├── authnginx
│ └── htpasswd
└── default.conf
├── .gitignore
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── screenshot-cards.png
├── screenshot-signup.png
├── screenshot-wizard.png
├── screenshot-dashboard.png
├── src
├── App.test.js
├── index.css
├── components
│ ├── ScrollTop.js
│ ├── Menu.js
│ ├── common
│ │ ├── Months.js
│ │ ├── Loading.js
│ │ └── Back.js
│ ├── SimpleLineChart.js
│ ├── typo
│ │ └── SectionHeader.js
│ ├── buttons
│ │ └── ButtonBar.js
│ ├── dialogs
│ │ ├── BaseDialog.js
│ │ ├── InstructionDialog.js
│ │ └── SwipeDialog.js
│ ├── Cards.js
│ ├── cards
│ │ └── CardItem.js
│ ├── Topbar.js
│ ├── Main.js
│ ├── Signup.js
│ ├── Dashboard.js
│ └── Wizard.js
├── index.js
├── App.css
├── images
│ ├── shape.svg
│ ├── back-arrow.svg
│ └── logo.svg
├── routes.js
├── App.js
└── serviceWorker.js
├── Dockerfile
├── .github
└── FUNDING.yml
├── LICENSE
├── package.json
└── README.md
/conf/authnginx/htpasswd:
--------------------------------------------------------------------------------
1 | dbprototest:saHJ36Omlhks2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | build/
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexanmtz/material-sense/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/screenshot-cards.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexanmtz/material-sense/HEAD/screenshot-cards.png
--------------------------------------------------------------------------------
/screenshot-signup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexanmtz/material-sense/HEAD/screenshot-signup.png
--------------------------------------------------------------------------------
/screenshot-wizard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexanmtz/material-sense/HEAD/screenshot-wizard.png
--------------------------------------------------------------------------------
/screenshot-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexanmtz/material-sense/HEAD/screenshot-dashboard.png
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/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 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/ScrollTop.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import { withRouter } from 'react-router-dom'
3 |
4 | class ScrollToTop extends Component {
5 | componentDidUpdate(prevProps) {
6 | if (this.props.location.pathname !== prevProps.location.pathname) {
7 | window.scrollTo(0, 0);
8 | }
9 | }
10 |
11 | render() {
12 | return this.props.children;
13 | }
14 | }
15 |
16 | export default withRouter(ScrollToTop);
17 |
--------------------------------------------------------------------------------
/conf/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name localhost;
4 |
5 | location / {
6 | root /usr/share/nginx/html;
7 | index index.html index.htm;
8 | try_files $uri /index.html;
9 | }
10 |
11 | auth_basic "auth required to access this website";
12 | auth_basic_user_file authnginx/htpasswd;
13 |
14 | error_page 500 502 503 504 /50x.html;
15 | location = /50x.html {
16 | root /usr/share/nginx/html;
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render( , document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: http://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # stage: 1
2 | FROM node:latest as react-build
3 |
4 | # Create app directory
5 | WORKDIR /app
6 |
7 | COPY . ./
8 |
9 | RUN yarn install
10 | RUN yarn build
11 |
12 | # stage: 2 — the production environment
13 | FROM nginx:alpine
14 | COPY --from=react-build /app/build /usr/share/nginx/html
15 |
16 | # To provide a http authentication comment out the next two lines
17 | #COPY conf/default.conf /etc/nginx/conf.d/default.conf
18 | #COPY conf/authnginx/htpasswd /etc/nginx/authnginx/htpasswd
19 | EXPOSE 80 2222
20 | CMD ["nginx", "-g", "daemon off;"]
21 |
--------------------------------------------------------------------------------
/src/components/Menu.js:
--------------------------------------------------------------------------------
1 | const Menu = [
2 | {
3 | label: "Home",
4 | pathname: "/"
5 | },
6 | {
7 | label: "Dashboard",
8 | pathname: "/dashboard"
9 | },
10 | {
11 | label: "Signup",
12 | pathname: "/signup"
13 | },
14 | {
15 | label: "Wizard",
16 | pathname: "/wizard"
17 | },
18 | {
19 | label: "Cards",
20 | pathname: "/cards"
21 | },
22 | {
23 | label: "Github",
24 | pathname: "https://github.com/alexanmtz/material-sense",
25 | external: true
26 | }
27 |
28 | ];
29 |
30 | export default Menu;
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | }
9 |
10 | .App-header {
11 | background-color: #282c34;
12 | min-height: 100vh;
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: calc(10px + 2vmin);
18 | color: white;
19 | }
20 |
21 | .App-link {
22 | color: #61dafb;
23 | }
24 |
25 | @keyframes App-logo-spin {
26 | from {
27 | transform: rotate(0deg);
28 | }
29 | to {
30 | transform: rotate(360deg);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [alexanmtz]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: alexanmtz
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with a single custom sponsorship URL
13 |
--------------------------------------------------------------------------------
/src/images/shape.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/components/common/Months.js:
--------------------------------------------------------------------------------
1 | const Months = [
2 | '01 December 2018',
3 | '01 January 2019',
4 | '01 February 2019',
5 | '01 March 2019',
6 | '01 April 2019',
7 | '01 May 2019',
8 | '01 June 2019',
9 | '01 July 2019',
10 | '01 August 2019',
11 | '01 September 2019',
12 | '01 October 2019',
13 | '01 November 2019',
14 | '01 December 2019',
15 | '01 January 2020',
16 | '01 March 2020',
17 | '01 April 2020',
18 | '01 May 2020',
19 | '01 June 2020',
20 | '01 July 2020',
21 | '01 August 2020',
22 | '01 September 2020',
23 | '01 October 2020',
24 | '01 November 2020',
25 | '01 December 2020',
26 | '01 January 2020',
27 | '01 February 2020',
28 | '01 March 2020',
29 | '01 April 2020',
30 | ];
31 |
32 | export default Months;
--------------------------------------------------------------------------------
/src/components/common/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Typography from '@material-ui/core/Typography';
3 | import withStyles from '@material-ui/styles/withStyles';
4 |
5 | const styles = theme => ({
6 | loadingMessage: {
7 | position: 'absolute',
8 | top: '40%',
9 | left: '40%'
10 | }
11 | });
12 |
13 | function Loading(props) {
14 | const { classes, loading } = props;
15 | return (
16 |
17 | 👋
18 |
19 | Waiting for input
20 |
21 |
22 | );
23 | }
24 |
25 | export default withStyles(styles)(Loading);
--------------------------------------------------------------------------------
/src/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Route, HashRouter, Switch } from 'react-router-dom'
3 | import Dashboard from './components/Dashboard'
4 | import Wizard from './components/Wizard'
5 | import Cards from './components/Cards'
6 | import Main from './components/Main'
7 | import Signup from './components/Signup'
8 | import ScrollToTop from './components/ScrollTop'
9 |
10 | export default props => (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | )
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { ThemeProvider } from '@material-ui/styles';
3 | import { createMuiTheme } from '@material-ui/core/styles'
4 | import './App.css';
5 | import Routes from './routes'
6 | import { blue, indigo } from '@material-ui/core/colors'
7 |
8 | const theme = createMuiTheme({
9 | palette: {
10 | secondary: {
11 | main: blue[900]
12 | },
13 | primary: {
14 | main: indigo[700]
15 | }
16 | },
17 | typography: {
18 | // Use the system font instead of the default Roboto font.
19 | fontFamily: [
20 | '"Lato"',
21 | 'sans-serif'
22 | ].join(',')
23 | }
24 | });
25 |
26 |
27 | class App extends Component {
28 | render() {
29 | return (
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 | }
38 |
39 | export default App;
40 |
--------------------------------------------------------------------------------
/src/components/SimpleLineChart.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ResponsiveContainer from 'recharts/lib/component/ResponsiveContainer';
3 | import BarChart from 'recharts/lib/chart/BarChart';
4 | import Bar from 'recharts/lib/cartesian/Bar';
5 | import XAxis from 'recharts/lib/cartesian/XAxis';
6 | import Tooltip from 'recharts/lib/component/Tooltip';
7 | import { withTheme } from '@material-ui/styles';
8 |
9 | function SimpleLineChart(props) {
10 | const { theme, data } = props;
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
23 | export default withTheme(SimpleLineChart);
--------------------------------------------------------------------------------
/src/components/typo/SectionHeader.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import withStyles from '@material-ui/styles/withStyles';
3 | import Typography from '@material-ui/core/Typography';
4 | import { withRouter } from 'react-router-dom';
5 |
6 | const styles = theme => ({
7 | sectionContainer: {
8 | marginTop: theme.spacing(4),
9 | marginBottom: theme.spacing(4)
10 | },
11 | title: {
12 | fontWeight: 'bold'
13 | }
14 | });
15 |
16 | class SectionHeader extends Component {
17 | render() {
18 | const { classes, title, subtitle} = this.props;
19 | return (
20 |
21 |
22 | {title}
23 |
24 |
25 | {subtitle}
26 |
27 |
28 | )
29 | }
30 | }
31 |
32 | export default withRouter(withStyles(styles)(SectionHeader));
33 |
--------------------------------------------------------------------------------
/src/components/buttons/ButtonBar.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import withStyles from '@material-ui/styles/withStyles';
3 | import Button from '@material-ui/core/Button';
4 |
5 | const styles = theme => ({
6 | primary: {
7 | marginRight: theme.spacing(2)
8 | },
9 | secondary: {
10 | background: theme.palette.secondary['100'],
11 | color: 'white'
12 | },
13 | spaceTop: {
14 | marginTop: 20
15 | }
16 | })
17 |
18 | class ButtonBar extends Component {
19 |
20 | render() {
21 | const { classes } = this.props;
22 |
23 | return (
24 |
25 |
28 | Delete
29 |
30 |
35 | Edit
36 |
37 |
38 | )
39 | }
40 | }
41 |
42 | export default withStyles(styles)(ButtonBar);
--------------------------------------------------------------------------------
/src/components/common/Back.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import withStyles from '@material-ui/styles/withStyles';
3 | import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft';
4 | import Typography from '@material-ui/core/Typography';
5 | import { Link, withRouter } from 'react-router-dom';
6 |
7 | const styles = theme => ({
8 | link: {
9 | textDecoration: 'none',
10 | color: 'inherit'
11 | },
12 | text: {
13 | display: 'inline-block',
14 | verticalAlign: 'text-bottom'
15 | }
16 | });
17 |
18 | class Back extends Component {
19 | render() {
20 | const { classes } = this.props;
21 | return (
22 |
23 |
24 |
25 |
26 | Back to Dashboard
27 |
28 |
29 |
30 | )
31 | }
32 | }
33 |
34 | export default withRouter(withStyles(styles)(Back));
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Alexandre Magno Teles Zimerer
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.
22 |
--------------------------------------------------------------------------------
/src/images/back-arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "material-sense",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "https://alexanmtz.github.io/material-sense",
6 | "dependencies": {
7 | "@material-ui/core": "^4.1.1",
8 | "@material-ui/icons": "^4.2.0",
9 | "@material-ui/lab": "^4.0.0-alpha.16",
10 | "@material-ui/styles": "^4.0.0",
11 | "gh-pages": "^2.0.1",
12 | "numeral": "^2.0.6",
13 | "opencollective-postinstall": "^2.0.2",
14 | "query-string": "^6.2.0",
15 | "react": "^16.8.6",
16 | "react-dom": "^16.8.6",
17 | "react-router-dom": "^5.0.1",
18 | "react-scripts": "3.0.1",
19 | "react-swipeable-views": "^0.13.0",
20 | "react-swipeable-views-utils": "^0.13.0",
21 | "recharts": "^1.3.5"
22 | },
23 | "scripts": {
24 | "start": "react-scripts start",
25 | "build": "react-scripts build",
26 | "test": "react-scripts test",
27 | "eject": "react-scripts eject",
28 | "predeploy": "npm run build",
29 | "deploy": "gh-pages -d build",
30 | "postinstall": "opencollective-postinstall || true"
31 | },
32 | "eslintConfig": {
33 | "extends": "react-app"
34 | },
35 | "browserslist": [
36 | ">0.2%",
37 | "not dead",
38 | "not ie <= 11",
39 | "not op_mini all"
40 | ],
41 | "collective": {
42 | "type": "opencollective",
43 | "url": "https://opencollective.com/material-sense"
44 | }
45 | }
--------------------------------------------------------------------------------
/src/components/dialogs/BaseDialog.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import withStyles from '@material-ui/styles/withStyles';
3 | import Dialog from '@material-ui/core/Dialog';
4 | import DialogContent from '@material-ui/core/DialogContent';
5 | import DialogContentText from '@material-ui/core/DialogContentText';
6 | import DialogTitle from '@material-ui/core/DialogTitle';
7 |
8 | const styles = theme => ({
9 | container: {
10 | maxWidth: 600,
11 | flexGrow: 1,
12 | textAlign: 'center',
13 | display: 'flex',
14 | flexDirection: 'column',
15 | justifyContent: 'center'
16 | },
17 | bottomMargin: {
18 | marginBottom: theme.spacing(2)
19 | }
20 | });
21 |
22 | class BaseDialog extends Component {
23 | render() {
24 | const { classes, open, onClose } = this.props;
25 | return (
26 |
33 |
34 |
35 |
36 |
37 | {this.props.children}
38 |
39 |
40 |
41 |
42 | )
43 | }
44 | }
45 |
46 | export default withStyles(styles)(BaseDialog);
--------------------------------------------------------------------------------
/src/components/dialogs/InstructionDialog.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link, withRouter } from 'react-router-dom';
3 | import withStyles from '@material-ui/styles/withStyles';
4 | import Typography from '@material-ui/core/Typography';
5 | import Button from '@material-ui/core/Button';
6 | import BaseDialog from './BaseDialog';
7 |
8 | const styles = theme => ({
9 | container: {
10 | maxWidth: 600,
11 | flexGrow: 1,
12 | textAlign: 'center',
13 | display: 'flex',
14 | flexDirection: 'column',
15 | justifyContent: 'center'
16 | },
17 | bottomMargin: {
18 | marginBottom: theme.spacing(2)
19 | }
20 | });
21 |
22 | class InstructionDialog extends Component {
23 | render() {
24 | const { classes } = this.props;
25 | return (
26 |
27 |
28 |
29 | This is a sample introduction
30 |
31 |
32 |
33 | Getting started
34 |
35 |
36 | Dashboard
37 |
38 |
39 | )
40 | }
41 | }
42 |
43 | export default withRouter(withStyles(styles)(InstructionDialog));
44 |
--------------------------------------------------------------------------------
/src/components/Cards.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import withStyles from "@material-ui/styles/withStyles";
3 | import CssBaseline from "@material-ui/core/CssBaseline";
4 | import Grid from "@material-ui/core/Grid";
5 | import CardItem from "./cards/CardItem";
6 | import Topbar from "./Topbar";
7 | import SectionHeader from "./typo/SectionHeader";
8 | const backgroundShape = require("../images/shape.svg");
9 |
10 | const styles = theme => ({
11 | root: {
12 | flexGrow: 1,
13 | backgroundColor: theme.palette.grey["A500"],
14 | overflow: "hidden",
15 | background: `url(${backgroundShape}) no-repeat`,
16 | backgroundSize: "cover",
17 | backgroundPosition: "0 400px",
18 | marginTop: 20,
19 | padding: 20,
20 | paddingBottom: 200
21 | },
22 | grid: {
23 | width: 1000
24 | }
25 | });
26 |
27 | class Cards extends Component {
28 | render() {
29 | const { classes } = this.props;
30 | const currentPath = this.props.location.pathname;
31 |
32 | return (
33 |
34 |
35 |
36 |
37 |
38 |
45 |
46 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default withStyles(styles)(Cards);
61 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
25 | Material Sense
26 |
27 |
28 |
29 | You need to enable JavaScript to run this app.
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/components/cards/CardItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import withStyles from '@material-ui/styles/withStyles';
3 | import Typography from '@material-ui/core/Typography';
4 | import Paper from '@material-ui/core/Paper';
5 | import Avatar from '@material-ui/core/Avatar';
6 | import DescriptionIcon from '@material-ui/icons/Description';
7 | import ButtonBar from '../buttons/ButtonBar';
8 |
9 | const styles = theme => ({
10 | paper: {
11 | padding: theme.spacing(3),
12 | textAlign: 'left',
13 | color: theme.palette.text.secondary
14 | },
15 | avatar: {
16 | margin: 10,
17 | backgroundColor: theme.palette.grey['200'],
18 | color: theme.palette.text.primary,
19 | },
20 | avatarContainer: {
21 | [theme.breakpoints.down('sm')]: {
22 | marginLeft: 0,
23 | marginBottom: theme.spacing(4)
24 | }
25 | },
26 | itemContainer: {
27 | display: 'flex',
28 | alignItems: 'center',
29 | justifyContent: 'flex-start',
30 | [theme.breakpoints.down('sm')]: {
31 | display: 'flex',
32 | flexDirection: 'column',
33 | justifyContent: 'center'
34 | }
35 | },
36 | baseline: {
37 | alignSelf: 'baseline',
38 | marginLeft: theme.spacing(4),
39 | [theme.breakpoints.down('sm')]: {
40 | display: 'flex',
41 | flexDirection: 'column',
42 | textAlign: 'center',
43 | alignItems: 'center',
44 | width: '100%',
45 | marginTop: theme.spacing(2),
46 | marginBottom: theme.spacing(2),
47 | marginLeft: 0
48 | }
49 | },
50 | inline: {
51 | display: 'inline-block',
52 | marginLeft: theme.spacing(4),
53 | [theme.breakpoints.down('sm')]: {
54 | marginLeft: 0
55 | }
56 | },
57 | inlineRight: {
58 | width: '30%',
59 | textAlign: 'right',
60 | marginLeft: 50,
61 | alignSelf: 'flex-end',
62 | [theme.breakpoints.down('sm')]: {
63 | width: '100%',
64 | margin: 0,
65 | textAlign: 'center'
66 | }
67 | },
68 | backButton: {
69 | marginRight: theme.spacing(2)
70 | }
71 | })
72 |
73 | class CardItem extends Component {
74 |
75 | render() {
76 | const { classes } = this.props;
77 |
78 | return (
79 |
80 |
81 |
82 |
87 |
88 |
89 |
90 | Months
91 |
92 |
93 | 4 month(s)
94 |
95 |
96 |
97 |
98 | Creation date
99 |
100 |
101 | 01 February 2019
102 |
103 |
104 |
105 |
106 | Amount
107 |
108 |
109 | 6,600 USD
110 |
111 |
112 |
113 |
114 |
115 | Other Amount
116 |
117 |
118 | Once a month
119 |
120 |
121 |
122 |
123 |
124 |
125 | )
126 | }
127 | }
128 |
129 | export default withStyles(styles)(CardItem);
130 |
--------------------------------------------------------------------------------
/src/components/dialogs/SwipeDialog.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link, withRouter } from 'react-router-dom';
3 | import withStyles from '@material-ui/styles/withStyles';
4 | import Typography from '@material-ui/core/Typography';
5 | import Button from '@material-ui/core/Button';
6 | import BaseDialog from './BaseDialog';
7 | import SwipeableViews from 'react-swipeable-views';
8 | import MobileStepper from '@material-ui/core/MobileStepper';
9 | import { autoPlay } from 'react-swipeable-views-utils';
10 |
11 | const logo = require('../../images/logo.svg');
12 |
13 | const AutoPlaySwipeableViews = autoPlay(SwipeableViews);
14 |
15 | const tutorialSteps = [
16 | {
17 | label: 'A first label',
18 | description: 'This is the first item on the label',
19 | imgPath:
20 | 'https://images.unsplash.com/photo-1537944434965-cf4679d1a598?auto=format&fit=crop&w=400&h=250&q=60',
21 | },
22 | {
23 | label: 'A second label',
24 | description: 'This is the second item on the label',
25 | imgPath:
26 | 'https://images.unsplash.com/photo-1538032746644-0212e812a9e7?auto=format&fit=crop&w=400&h=250&q=60',
27 | },
28 | {
29 | label: 'A third label',
30 | description: 'This is the third item on the label',
31 | imgPath:
32 | 'https://images.unsplash.com/photo-1537996194471-e657df975ab4?auto=format&fit=crop&w=400&h=250&q=80',
33 | },
34 | {
35 | label: 'A fifth label',
36 | description: 'This is the fifth item on the label',
37 | imgPath:
38 | 'https://images.unsplash.com/photo-1518732714860-b62714ce0c59?auto=format&fit=crop&w=400&h=250&q=60',
39 | },
40 | {
41 | label: 'Other label',
42 | description: 'This is other label',
43 | imgPath:
44 | 'https://images.unsplash.com/photo-1512341689857-198e7e2f3ca8?auto=format&fit=crop&w=400&h=250&q=60',
45 | },
46 | ];
47 |
48 | const styles = theme => ({
49 | container: {
50 | maxWidth: 600,
51 | flexGrow: 1,
52 | textAlign: 'center',
53 | display: 'flex',
54 | flexDirection: 'column',
55 | justifyContent: 'center'
56 | },
57 | stepsContainer: {
58 | marginLeft: 72,
59 | textAlign: 'left',
60 | marginTop: 20,
61 | height: 65
62 | },
63 | bottomMargin: {
64 | marginBottom: theme.spacing(2)
65 | }
66 | });
67 |
68 | class SwipeDialog extends Component {
69 |
70 | state = {
71 | activeStep: 0
72 | }
73 |
74 | handleNext = () => {
75 | this.setState(prevState => ({
76 | activeStep: prevState.activeStep + 1,
77 | }));
78 | };
79 |
80 | handleBack = () => {
81 | this.setState(prevState => ({
82 | activeStep: prevState.activeStep - 1,
83 | }));
84 | };
85 |
86 | handleStepChange = activeStep => {
87 | this.setState({ activeStep });
88 | };
89 |
90 | render() {
91 | const { classes } = this.props;
92 | const maxSteps = tutorialSteps.length;
93 | const { activeStep } = this.state;
94 | return (
95 |
96 |
97 |
98 |
99 |
100 |
101 |
107 | {tutorialSteps.map((step, index) => (
108 |
109 | {Math.abs(activeStep - index) <= 2 ? (
110 |
111 | ) : null}
112 |
113 | ))}
114 |
115 |
122 | Next
123 |
124 | }
125 | backButton={
126 |
127 | Back
128 |
129 | }
130 | />
131 |
132 |
133 |
134 | {tutorialSteps[activeStep].label}
135 |
136 |
137 | {tutorialSteps[activeStep].description}
138 |
139 |
140 |
141 |
142 | Getting started
143 |
144 |
145 |
146 |
147 | )
148 | }
149 | }
150 |
151 | export default withRouter(withStyles(styles)(SwipeDialog));
152 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
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 http://bit.ly/CRA-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.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### If you're using this template, please add below your project and send a PR:
2 | - Alexandre Magno (http://ahorta.io)
3 | - Victoria Botelho Martins (https://contech-cbs.web.app/)
4 |
5 | ### Help to fund this project
6 | https://opencollective.com/material-sense
7 |
8 | #### Projects using this template:
9 | - Ahorta Client: https://github.com/worknenjoy/ahorta-client
10 |
11 |
12 | # React Material UI template - Material Sense
13 | A full simple application for react [material ui](https://material-ui.com/)
14 |
15 | ## Features
16 | - Responsive
17 | - Include a Graph using [recharts](https://github.com/recharts/recharts)
18 | - With [Router](https://github.com/ReactTraining/react-router) included
19 | - A docker container for production build
20 | - Created with [Create react app](https://github.com/facebook/create-react-app)
21 |
22 | 
23 |
24 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
25 |
26 | ## Available Scripts
27 |
28 | In the project directory, you can run:
29 |
30 | ### `yarn start`
31 |
32 | Runs the app in the development mode.
33 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
34 |
35 | The page will reload if you make edits.
36 | You will also see any lint errors in the console.
37 |
38 | ### `npm test`
39 |
40 | Launches the test runner in the interactive watch mode.
41 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
42 |
43 | ### `npm run build`
44 |
45 | Builds the app for production to the `build` folder.
46 | It correctly bundles React in production mode and optimizes the build for the best performance.
47 |
48 | The build is minified and the filenames include the hashes.
49 | Your app is ready to be deployed!
50 |
51 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
52 |
53 | ### `npm run eject`
54 |
55 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
56 |
57 | 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.
58 |
59 | 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.
60 |
61 | 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.
62 |
63 | ## Learn More
64 |
65 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
66 |
67 | To learn React, check out the [React documentation](https://reactjs.org/).
68 |
69 | ### Docker
70 |
71 | This project works in a docker container as well
72 |
73 | First run:
74 | `docker build . -t material-sense`
75 |
76 | Then:
77 | `docker run -p 2222:2222 material-sense`
78 |
79 | _the 2222 port intend to make work on Azure websites as container for default, cause is the port they use to expose the server_
80 |
81 | ### Publish at Github pages
82 | `yarn deploy`
83 |
84 | ## Screenshots
85 | 
86 | 
87 | 
88 | 
89 |
90 | ## Contributors
91 |
92 | ### Code Contributors
93 |
94 | This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
95 |
96 |
97 | ### Financial Contributors
98 |
99 | Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/material-sense/contribute)]
100 |
101 | #### Individuals
102 |
103 |
104 |
105 | #### Organizations
106 |
107 | Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/material-sense/contribute)]
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/components/Topbar.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import withStyles from "@material-ui/styles/withStyles";
3 | import { Link, withRouter } from "react-router-dom";
4 | import Grid from "@material-ui/core/Grid";
5 | import Typography from "@material-ui/core/Typography";
6 | import Toolbar from "@material-ui/core/Toolbar";
7 | import AppBar from "@material-ui/core/AppBar";
8 | import Tabs from "@material-ui/core/Tabs";
9 | import Tab from "@material-ui/core/Tab";
10 | import MenuIcon from "@material-ui/icons/Menu";
11 | import IconButton from "@material-ui/core/IconButton";
12 | import List from "@material-ui/core/List";
13 | import ListItem from "@material-ui/core/ListItem";
14 | import ListItemText from "@material-ui/core/ListItemText";
15 | import SwipeableDrawer from "@material-ui/core/SwipeableDrawer";
16 | import { Link as MaterialLink } from "@material-ui/core";
17 | import Menu from "./Menu";
18 |
19 | const logo = require("../images/logo.svg");
20 |
21 | const styles = theme => ({
22 | appBar: {
23 | position: "relative",
24 | boxShadow: "none",
25 | borderBottom: `1px solid ${theme.palette.grey["100"]}`,
26 | backgroundColor: "white"
27 | },
28 | inline: {
29 | display: "inline"
30 | },
31 | flex: {
32 | display: "flex",
33 | [theme.breakpoints.down("sm")]: {
34 | display: "flex",
35 | justifyContent: "space-evenly",
36 | alignItems: "center"
37 | }
38 | },
39 | link: {
40 | textDecoration: "none",
41 | color: "inherit"
42 | },
43 | productLogo: {
44 | display: "inline-block",
45 | borderLeft: `1px solid ${theme.palette.grey["A100"]}`,
46 | marginLeft: 32,
47 | paddingLeft: 24,
48 | [theme.breakpoints.up("md")]: {
49 | paddingTop: "1.5em"
50 | }
51 | },
52 | tagline: {
53 | display: "inline-block",
54 | marginLeft: 10,
55 | [theme.breakpoints.up("md")]: {
56 | paddingTop: "0.8em"
57 | }
58 | },
59 | iconContainer: {
60 | display: "none",
61 | [theme.breakpoints.down("sm")]: {
62 | display: "flex",
63 | justifyContent: 'center',
64 | alignItems: 'center',
65 | }
66 | },
67 | iconButton: {
68 | float: "right"
69 | },
70 | tabContainer: {
71 | marginLeft: 32,
72 | [theme.breakpoints.down("sm")]: {
73 | display: "none"
74 | }
75 | },
76 | tabItem: {
77 | paddingTop: 20,
78 | paddingBottom: 20,
79 | minWidth: "auto"
80 | }
81 | });
82 |
83 | class Topbar extends Component {
84 | state = {
85 | value: 0,
86 | menuDrawer: false
87 | };
88 |
89 | handleChange = (event, value) => {
90 | this.setState({ value });
91 | };
92 |
93 | mobileMenuOpen = event => {
94 | this.setState({ menuDrawer: true });
95 | };
96 |
97 | mobileMenuClose = event => {
98 | this.setState({ menuDrawer: false });
99 | };
100 |
101 | componentDidMount() {
102 | window.scrollTo(0, 0);
103 | }
104 |
105 | current = () => {
106 | if (this.props.currentPath === "/home") {
107 | return 0;
108 | }
109 | if (this.props.currentPath === "/dashboard") {
110 | return 1;
111 | }
112 | if (this.props.currentPath === "/signup") {
113 | return 2;
114 | }
115 | if (this.props.currentPath === "/wizard") {
116 | return 3;
117 | }
118 | if (this.props.currentPath === "/cards") {
119 | return 4;
120 | }
121 | };
122 |
123 | render() {
124 | const { classes } = this.props;
125 |
126 | return (
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | Material Sense
136 |
137 |
138 |
139 | {!this.props.noTabs && (
140 |
141 |
142 | A material UI Template
143 |
144 |
145 |
151 |
152 |
153 |
154 |
155 |
161 |
162 |
163 | {Menu.map((item, index) => (
164 |
178 |
179 |
180 | ))}
181 |
182 |
183 |
189 | {Menu.map((item, index) => (
190 |
205 | ))}
206 |
207 |
208 |
209 | )}
210 |
211 |
212 |
213 |
214 | );
215 | }
216 | }
217 |
218 | export default withRouter(withStyles(styles)(Topbar));
219 |
--------------------------------------------------------------------------------
/src/components/Main.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import withStyles from "@material-ui/styles/withStyles";
3 | import { withRouter } from "react-router-dom";
4 | import CssBaseline from "@material-ui/core/CssBaseline";
5 | import Paper from "@material-ui/core/Paper";
6 | import Typography from "@material-ui/core/Typography";
7 | import Grid from "@material-ui/core/Grid";
8 | import Button from "@material-ui/core/Button";
9 | import InstructionDialog from "./dialogs/InstructionDialog";
10 | import SwipeDialog from "./dialogs/SwipeDialog";
11 |
12 | import Topbar from "./Topbar";
13 |
14 | const backgroundShape = require("../images/shape.svg");
15 |
16 | const styles = theme => ({
17 | root: {
18 | flexGrow: 1,
19 | backgroundColor: theme.palette.grey["100"],
20 | overflow: "hidden",
21 | background: `url(${backgroundShape}) no-repeat`,
22 | backgroundSize: "cover",
23 | backgroundPosition: "0 400px",
24 | paddingBottom: 200
25 | },
26 | grid: {
27 | width: 1200,
28 | marginTop: 40,
29 | [theme.breakpoints.down("sm")]: {
30 | width: "calc(100% - 20px)"
31 | }
32 | },
33 | paper: {
34 | padding: theme.spacing(3),
35 | textAlign: "left",
36 | color: theme.palette.text.secondary
37 | },
38 | rangeLabel: {
39 | display: "flex",
40 | justifyContent: "space-between",
41 | paddingTop: theme.spacing(2)
42 | },
43 | topBar: {
44 | display: "flex",
45 | justifyContent: "space-between",
46 | alignItems: "center",
47 | marginTop: 32
48 | },
49 | outlinedButtom: {
50 | textTransform: "uppercase",
51 | margin: theme.spacing(1)
52 | },
53 | actionButtom: {
54 | textTransform: "uppercase",
55 | margin: theme.spacing(1),
56 | width: 152
57 | },
58 | blockCenter: {
59 | padding: theme.spacing(2),
60 | textAlign: "center"
61 | },
62 | block: {
63 | padding: theme.spacing(2)
64 | },
65 | box: {
66 | marginBottom: 40,
67 | height: 65
68 | },
69 | inlining: {
70 | display: "inline-block",
71 | marginRight: 10
72 | },
73 | buttonBar: {
74 | display: "flex"
75 | },
76 | alignRight: {
77 | display: "flex",
78 | justifyContent: "flex-end"
79 | },
80 | noBorder: {
81 | borderBottomStyle: "hidden"
82 | },
83 | loadingState: {
84 | opacity: 0.05
85 | },
86 | loadingMessage: {
87 | position: "absolute",
88 | top: "40%",
89 | left: "40%"
90 | }
91 | });
92 |
93 | class Main extends Component {
94 | state = {
95 | learnMoredialog: false,
96 | getStartedDialog: false
97 | };
98 |
99 | componentDidMount() {}
100 |
101 | openDialog = event => {
102 | this.setState({ learnMoredialog: true });
103 | };
104 |
105 | dialogClose = event => {
106 | this.setState({ learnMoredialog: false });
107 | };
108 |
109 | openGetStartedDialog = event => {
110 | this.setState({ getStartedDialog: true });
111 | };
112 |
113 | closeGetStartedDialog = event => {
114 | this.setState({ getStartedDialog: false });
115 | };
116 |
117 | render() {
118 | const { classes } = this.props;
119 | return (
120 |
121 |
122 |
123 |
124 |
125 |
132 |
133 |
134 |
135 |
140 | First title
141 |
142 |
143 | A first title style with two lines
144 |
145 |
146 |
147 |
152 | Learn more
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
165 | Another box
166 |
167 |
168 | A default box
169 |
170 |
171 |
172 |
177 | Learn more
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
190 | A box with a carousel
191 |
192 |
193 | If you click in Getting Started, you will see a nice
194 | carousel
195 |
196 |
197 |
198 |
203 | Learn more
204 |
205 |
211 | Dashboard
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 | Full box
223 |
224 |
225 | This is an example of a full-width box
226 |
227 |
228 |
229 |
234 | Learn more
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
247 |
251 |
252 |
253 | );
254 | }
255 | }
256 |
257 | export default withRouter(withStyles(styles)(Main));
258 |
--------------------------------------------------------------------------------
/src/components/Signup.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import withStyles from "@material-ui/styles/withStyles";
3 | import { withRouter } from "react-router-dom";
4 | import CssBaseline from "@material-ui/core/CssBaseline";
5 | import Typography from "@material-ui/core/Typography";
6 | import Grid from "@material-ui/core/Grid";
7 | import Paper from "@material-ui/core/Paper";
8 | import Button from "@material-ui/core/Button";
9 | import Stepper from "@material-ui/core/Stepper";
10 | import Step from "@material-ui/core/Step";
11 | import StepLabel from "@material-ui/core/StepLabel";
12 | import OutlinedInput from "@material-ui/core/OutlinedInput";
13 | import MenuItem from "@material-ui/core/MenuItem";
14 | import FormControl from "@material-ui/core/FormControl";
15 | import Select from "@material-ui/core/Select";
16 | import List from "@material-ui/core/List";
17 | import ListItem from "@material-ui/core/ListItem";
18 | import ListItemIcon from "@material-ui/core/ListItemIcon";
19 | import ListItemText from "@material-ui/core/ListItemText";
20 | import DoneIcon from "@material-ui/icons/Done";
21 | import CircularProgress from "@material-ui/core/CircularProgress";
22 | import Fade from "@material-ui/core/Fade";
23 | import Back from "./common/Back";
24 |
25 | const backgroundShape = require("../images/shape.svg");
26 |
27 | const logo = require("../images/logo.svg");
28 |
29 | const numeral = require("numeral");
30 | numeral.defaultFormat("0");
31 |
32 | const styles = theme => ({
33 | root: {
34 | flexGrow: 1,
35 | backgroundColor: theme.palette.secondary["A100"],
36 | overflow: "hidden",
37 | background: `url(${backgroundShape}) no-repeat`,
38 | backgroundSize: "cover",
39 | backgroundPosition: "0 400px",
40 | marginTop: 10,
41 | padding: 20,
42 | paddingBottom: 500
43 | },
44 | grid: {
45 | margin: `0 ${theme.spacing(2)}px`
46 | },
47 | smallContainer: {
48 | width: "60%"
49 | },
50 | bigContainer: {
51 | width: "80%"
52 | },
53 | logo: {
54 | marginBottom: 24,
55 | display: "flex",
56 | justifyContent: "center"
57 | },
58 | stepContainer: {
59 | display: "flex",
60 | flexDirection: "column",
61 | alignItems: "center"
62 | },
63 | stepGrid: {
64 | width: "80%"
65 | },
66 | buttonBar: {
67 | marginTop: 32,
68 | display: "flex",
69 | justifyContent: "center"
70 | },
71 | button: {
72 | backgroundColor: theme.palette.primary["A100"]
73 | },
74 | backButton: {
75 | marginRight: theme.spacing(1)
76 | },
77 | outlinedButtom: {
78 | textTransform: "uppercase",
79 | margin: theme.spacing(1)
80 | },
81 | stepper: {
82 | backgroundColor: "transparent"
83 | },
84 | paper: {
85 | padding: theme.spacing(3),
86 | textAlign: "left",
87 | color: theme.palette.text.secondary
88 | },
89 | topInfo: {
90 | display: "flex",
91 | justifyContent: "space-between",
92 | alignItems: "center",
93 | marginBottom: 42
94 | },
95 | formControl: {
96 | width: "100%"
97 | },
98 | selectEmpty: {
99 | marginTop: theme.spacing(2)
100 | }
101 | });
102 |
103 | const getSteps = () => {
104 | return ["User", "Signin", "Permission"];
105 | };
106 |
107 | class Signup extends Component {
108 | state = {
109 | activeStep: 0,
110 | receivingAccount: "",
111 | termsChecked: false,
112 | loading: true,
113 | labelWidth: 0
114 | };
115 |
116 | handleNext = () => {
117 | this.setState(state => ({
118 | activeStep: state.activeStep + 1
119 | }));
120 | if (this.state.activeStep === 2) {
121 | setTimeout(() => this.props.history.push("/dashboard"), 5000);
122 | }
123 | };
124 |
125 | handleBack = () => {
126 | this.setState(state => ({
127 | activeStep: state.activeStep - 1
128 | }));
129 | };
130 |
131 | handleReset = () => {
132 | this.setState({
133 | activeStep: 0
134 | });
135 | };
136 |
137 | handleChange = event => {
138 | this.setState({ [event.target.name]: event.target.value });
139 | };
140 |
141 | handleTerms = event => {
142 | this.setState({ termsChecked: event.target.checked });
143 | };
144 |
145 | stepActions() {
146 | if (this.state.activeStep === 0) {
147 | return "Sign in";
148 | }
149 | if (this.state.activeStep === 1) {
150 | return "Next";
151 | }
152 | if (this.state.activeStep === 2) {
153 | return "Accept";
154 | }
155 | return "Next";
156 | }
157 |
158 | render() {
159 | const { classes } = this.props;
160 | const steps = getSteps();
161 | const { activeStep, loading } = this.state;
162 |
163 | return (
164 |
165 |
166 |
167 |
168 |
169 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
187 | {steps.map(label => {
188 | return (
189 |
190 | {label}
191 |
192 | );
193 | })}
194 |
195 |
196 | {activeStep === 0 && (
197 |
198 |
199 |
200 |
201 |
206 | Select
207 |
208 |
209 | A item to select
210 |
211 |
212 |
213 |
221 | First options
222 |
223 |
227 |
235 | }
236 | >
237 |
238 | Select some option
239 |
240 | Option 1
241 |
242 | Other option
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 | )}
251 | {activeStep === 1 && (
252 |
253 |
254 |
255 |
256 |
257 | Sign & confirm
258 |
259 |
260 | Sign and confirm loan agreement
261 |
262 |
263 | One text to explain that
264 |
265 |
266 |
267 |
268 |
269 | )}
270 | {activeStep === 2 && (
271 |
272 |
273 |
274 |
275 |
276 | Permissions
277 |
278 |
279 | We need some permissions to proceed.
280 |
281 |
282 |
283 |
284 | Accounts
285 |
286 |
287 |
288 |
289 |
290 |
291 |
295 |
296 |
297 |
298 |
299 |
300 |
304 |
305 |
306 |
307 |
308 |
309 |
310 | )}
311 | {activeStep === 3 && (
312 |
313 |
314 |
317 |
318 |
319 |
324 | Collecting your data
325 |
326 |
327 | We are processing your request
328 |
329 |
330 |
331 |
338 |
345 |
346 |
347 |
348 |
349 |
350 |
351 | )}
352 | {activeStep !== 3 && (
353 |
354 | {activeStep !== 2 ? (
355 |
361 | Back
362 |
363 | ) : (
364 |
370 | Cancel
371 |
372 | )}
373 |
385 | {this.stepActions()}
386 |
387 |
388 | )}
389 |
390 |
391 |
392 |
393 |
394 |
395 | );
396 | }
397 | }
398 |
399 | export default withRouter(withStyles(styles)(Signup));
400 |
--------------------------------------------------------------------------------
/src/components/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import withStyles from "@material-ui/styles/withStyles";
3 | import { withRouter, Link } from "react-router-dom";
4 | import CssBaseline from "@material-ui/core/CssBaseline";
5 | import Paper from "@material-ui/core/Paper";
6 | import Typography from "@material-ui/core/Typography";
7 | import Grid from "@material-ui/core/Grid";
8 | import Slider from "@material-ui/core/Slider";
9 | import Button from "@material-ui/core/Button";
10 | import Avatar from "@material-ui/core/Avatar";
11 | import SimpleLineChart from "./SimpleLineChart";
12 | import Months from "./common/Months";
13 | import VerifiedUserIcon from "@material-ui/icons/VerifiedUser";
14 | import Loading from "./common/Loading";
15 |
16 | import Topbar from "./Topbar";
17 |
18 | const numeral = require("numeral");
19 | numeral.defaultFormat("0,000");
20 |
21 | const backgroundShape = require("../images/shape.svg");
22 |
23 | const styles = theme => ({
24 | root: {
25 | flexGrow: 1,
26 | backgroundColor: theme.palette.grey["100"],
27 | overflow: "hidden",
28 | background: `url(${backgroundShape}) no-repeat`,
29 | backgroundSize: "cover",
30 | backgroundPosition: "0 400px",
31 | paddingBottom: 200
32 | },
33 | grid: {
34 | width: 1200,
35 | margin: `0 ${theme.spacing(2)}px`,
36 | [theme.breakpoints.down("sm")]: {
37 | width: "calc(100% - 20px)"
38 | }
39 | },
40 | loadingState: {
41 | opacity: 0.05
42 | },
43 | paper: {
44 | padding: theme.spacing(3),
45 | margin: theme.spacing(2),
46 | textAlign: "left",
47 | color: theme.palette.text.secondary
48 | },
49 | rangeLabel: {
50 | display: "flex",
51 | justifyContent: "space-between",
52 | paddingTop: theme.spacing(2)
53 | },
54 | topBar: {
55 | display: "flex",
56 | justifyContent: "space-between",
57 | alignItems: "center"
58 | },
59 | outlinedButton: {
60 | textTransform: "uppercase",
61 | margin: theme.spacing(1)
62 | },
63 | actionButton: {
64 | textTransform: "uppercase",
65 | margin: theme.spacing(1),
66 | width: 152,
67 | height: 36
68 | },
69 | blockCenter: {
70 | padding: theme.spacing(2),
71 | textAlign: "center"
72 | },
73 | block: {
74 | padding: theme.spacing(2)
75 | },
76 | loanAvatar: {
77 | display: "inline-block",
78 | verticalAlign: "center",
79 | width: 16,
80 | height: 16,
81 | marginRight: 10,
82 | marginBottom: -2,
83 | color: theme.palette.primary.contrastText,
84 | backgroundColor: theme.palette.primary.main
85 | },
86 | interestAvatar: {
87 | display: "inline-block",
88 | verticalAlign: "center",
89 | width: 16,
90 | height: 16,
91 | marginRight: 10,
92 | marginBottom: -2,
93 | color: theme.palette.primary.contrastText,
94 | backgroundColor: theme.palette.primary.light
95 | },
96 | inlining: {
97 | display: "inline-block",
98 | marginRight: 10
99 | },
100 | buttonBar: {
101 | display: "flex"
102 | },
103 | noBorder: {
104 | borderBottomStyle: "hidden"
105 | },
106 | mainBadge: {
107 | textAlign: "center",
108 | marginTop: theme.spacing(4),
109 | marginBottom: theme.spacing(4)
110 | }
111 | });
112 |
113 | const monthRange = Months;
114 |
115 | class Dashboard extends Component {
116 | state = {
117 | loading: true,
118 | amount: 15000,
119 | period: 3,
120 | start: 0,
121 | monthlyInterest: 0,
122 | totalInterest: 0,
123 | monthlyPayment: 0,
124 | totalPayment: 0,
125 | data: []
126 | };
127 |
128 | updateValues() {
129 | const { amount, period, start } = this.state;
130 | const monthlyInterest =
131 | (amount * Math.pow(0.01 * 1.01, period)) / Math.pow(0.01, period - 1);
132 | const totalInterest = monthlyInterest * (period + start);
133 | const totalPayment = amount + totalInterest;
134 | const monthlyPayment =
135 | period > start ? totalPayment / (period - start) : totalPayment / period;
136 |
137 | const data = Array.from({ length: period + start }, (value, i) => {
138 | const delayed = i < start;
139 | return {
140 | name: monthRange[i],
141 | Type: delayed ? 0 : Math.ceil(monthlyPayment).toFixed(0),
142 | OtherType: Math.ceil(monthlyInterest).toFixed(0)
143 | };
144 | });
145 |
146 | this.setState({
147 | monthlyInterest,
148 | totalInterest,
149 | totalPayment,
150 | monthlyPayment,
151 | data
152 | });
153 | }
154 |
155 | componentDidMount() {
156 | this.updateValues();
157 | }
158 |
159 | handleChangeAmount = (event, value) => {
160 | this.setState({ amount: value, loading: false });
161 | this.updateValues();
162 | };
163 |
164 | handleChangePeriod = (event, value) => {
165 | this.setState({ period: value, loading: false });
166 | this.updateValues();
167 | };
168 |
169 | handleChangeStart = (event, value) => {
170 | this.setState({ start: value, loading: false });
171 | this.updateValues();
172 | };
173 |
174 | render() {
175 | const { classes } = this.props;
176 | const {
177 | amount,
178 | period,
179 | start,
180 | monthlyPayment,
181 | monthlyInterest,
182 | data,
183 | loading
184 | } = this.state;
185 | const currentPath = this.props.location.pathname;
186 |
187 | return (
188 |
189 |
190 |
191 |
192 |
193 |
200 |
201 |
202 |
203 |
204 | Dashboard
205 |
206 |
207 | Adjust and play with our sliders.
208 |
209 |
210 |
211 |
215 | Get help
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | How much you want to transfer
225 |
226 |
227 | Use slider to set the amount you need.
228 |
229 |
230 |
231 | {numeral(amount).format()} USD
232 |
233 |
234 |
235 |
242 |
243 |
244 |
245 | 15,000 USD
246 |
247 |
248 | 150,000 USD
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 | Period
259 |
260 |
A sample period
261 |
262 |
263 | {period} months
264 |
265 |
266 |
267 |
274 |
275 |
276 |
277 | 1 month
278 |
279 |
280 | 6 months
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 | Start date
291 |
292 |
293 | Set your preferred start date.
294 |
295 |
296 |
297 | {monthRange[start]}
298 |
299 |
300 |
301 |
308 |
309 |
310 |
311 | Dec 2018
312 |
313 |
314 | May 2019
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
326 |
327 |
328 |
329 | Some details
330 |
331 |
332 | Details about the graph
333 |
334 |
335 |
336 |
337 |
342 | Type
343 |
344 |
350 | {numeral(monthlyPayment).format()} units
351 |
352 |
353 |
354 |
355 |
360 | Othe type
361 |
362 |
368 | {numeral(monthlyInterest).format()} units
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
383 |
384 |
385 |
386 | State
387 |
388 |
389 |
394 |
399 | Verified
400 |
401 |
402 |
403 |
409 | Save
410 |
411 |
418 | Apply
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 | );
430 | }
431 | }
432 |
433 | export default withRouter(withStyles(styles)(Dashboard));
434 |
--------------------------------------------------------------------------------
/src/components/Wizard.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import withStyles from "@material-ui/styles/withStyles";
3 | import { withRouter } from "react-router-dom";
4 | import CssBaseline from "@material-ui/core/CssBaseline";
5 | import Typography from "@material-ui/core/Typography";
6 | import Grid from "@material-ui/core/Grid";
7 | import Paper from "@material-ui/core/Paper";
8 | import Button from "@material-ui/core/Button";
9 | import Stepper from "@material-ui/core/Stepper";
10 | import Step from "@material-ui/core/Step";
11 | import StepLabel from "@material-ui/core/StepLabel";
12 | import OutlinedInput from "@material-ui/core/OutlinedInput";
13 | import FormGroup from "@material-ui/core/FormGroup";
14 | import FormControlLabel from "@material-ui/core/FormControlLabel";
15 | import Checkbox from "@material-ui/core/Checkbox";
16 | import MenuItem from "@material-ui/core/MenuItem";
17 | import FormControl from "@material-ui/core/FormControl";
18 | import Select from "@material-ui/core/Select";
19 | import Back from "./common/Back";
20 |
21 | const qs = require("query-string");
22 | const backgroundShape = require("../images/shape.svg");
23 |
24 | const numeral = require("numeral");
25 | numeral.defaultFormat("0,000");
26 |
27 | const styles = theme => ({
28 | root: {
29 | flexGrow: 1,
30 | backgroundColor: theme.palette.primary["A100"],
31 | overflow: "hidden",
32 | background: `url(${backgroundShape}) no-repeat`,
33 | backgroundSize: "cover",
34 | backgroundPosition: "0 400px",
35 | marginTop: 10,
36 | padding: 20,
37 | paddingBottom: 200
38 | },
39 | grid: {
40 | margin: `0 ${theme.spacing(2)}px`
41 | },
42 | smallContainer: {
43 | width: "60%"
44 | },
45 | bigContainer: {
46 | width: "80%"
47 | },
48 | stepContainer: {
49 | display: "flex",
50 | flexDirection: "column",
51 | alignItems: "center"
52 | },
53 | stepGrid: {
54 | width: "80%"
55 | },
56 | backButton: {
57 | marginRight: theme.spacing(1)
58 | },
59 | outlinedButtom: {
60 | textTransform: "uppercase",
61 | margin: theme.spacing(1)
62 | },
63 | stepper: {
64 | backgroundColor: "transparent"
65 | },
66 | paper: {
67 | padding: theme.spacing(3),
68 | textAlign: "left",
69 | color: theme.palette.text.secondary
70 | },
71 | topInfo: {
72 | display: "flex",
73 | justifyContent: "space-between",
74 | alignItems: "center",
75 | marginBottom: 42
76 | },
77 | formControl: {
78 | width: "100%"
79 | },
80 | selectEmpty: {
81 | marginTop: theme.spacing(2)
82 | },
83 | borderColumn: {
84 | borderBottom: `1px solid ${theme.palette.grey["100"]}`,
85 | paddingBottom: 24,
86 | marginBottom: 24
87 | },
88 | flexBar: {
89 | marginTop: 32,
90 | display: "flex",
91 | justifyContent: "center"
92 | }
93 | });
94 |
95 | const getSteps = () => {
96 | return ["Info", "Bank", "Loan details", "Terms", "Confirm", "Done"];
97 | };
98 |
99 | class Wizard extends Component {
100 | state = {
101 | activeStep: 0,
102 | receivingAccount: "Home Account",
103 | repaimentAccount: "Saving Account",
104 | termsChecked: false,
105 | labelWidth: 0
106 | };
107 |
108 | componentDidMount() {}
109 |
110 | handleNext = () => {
111 | this.setState(state => ({
112 | activeStep: state.activeStep + 1
113 | }));
114 | };
115 |
116 | handleBack = () => {
117 | this.setState(state => ({
118 | activeStep: state.activeStep - 1
119 | }));
120 | };
121 |
122 | handleReset = () => {
123 | this.setState({
124 | activeStep: 0
125 | });
126 | };
127 |
128 | handleChange = event => {
129 | this.setState({ [event.target.name]: event.target.value });
130 | };
131 |
132 | handleTerms = event => {
133 | this.setState({ termsChecked: event.target.checked });
134 | };
135 |
136 | stepActions() {
137 | if (this.state.activeStep === 3) {
138 | return "Accept";
139 | }
140 | if (this.state.activeStep === 4) {
141 | return "Send";
142 | }
143 | if (this.state.activeStep === 5) {
144 | return "Done";
145 | }
146 | return "Next";
147 | }
148 |
149 | goToDashboard = event => {
150 | const queryString = this.props.location.search;
151 |
152 | this.props.history.push({
153 | pathname: "/dashboard",
154 | search: queryString
155 | });
156 | };
157 |
158 | render() {
159 | const { classes } = this.props;
160 | const queryString = this.props.location.search;
161 | const parsed = queryString ? qs.parse(queryString) : {};
162 | const steps = getSteps();
163 | const { activeStep } = this.state;
164 |
165 | return (
166 |
167 |
168 |
169 |
170 |
177 |
178 |
179 |
180 |
181 |
186 | {steps.map(label => {
187 | return (
188 |
189 | {label}
190 |
191 | );
192 | })}
193 |
194 |
195 | {activeStep === 0 && (
196 |
197 |
198 |
199 |
200 |
205 | Information
206 |
207 |
208 | General information about the service
209 |
210 |
211 |
212 |
217 | Edit
218 |
219 |
220 |
221 |
222 |
223 |
228 | User
229 |
230 |
231 | John Doe
232 |
233 |
234 |
235 |
240 | City
241 |
242 |
243 | Tokyo
244 |
245 |
246 |
247 |
248 |
249 | )}
250 | {activeStep === 1 && (
251 |
252 |
253 |
254 |
255 |
260 | Bank information
261 |
262 |
263 | Select account to receive the money
264 |
265 |
266 |
267 |
272 | Bank
273 |
274 |
275 | N26
276 |
277 |
278 |
279 |
287 | Receiving account
288 |
289 |
293 |
301 | }
302 | >
303 |
304 |
305 |
306 |
307 | First account
308 |
309 |
310 | Second account
311 |
312 |
313 | Third account
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 | )}
322 | {activeStep === 2 && (
323 |
324 |
325 |
326 |
327 |
332 | Details
333 |
334 |
335 | We need some details about any information
336 |
337 |
338 |
339 |
344 | Edit
345 |
346 |
347 |
348 |
349 |
355 |
356 |
361 | Amount
362 |
363 |
364 | {parsed
365 | ? numeral(parsed.amount).format()
366 | : "75,000"}{" "}
367 | DKK
368 |
369 |
370 |
371 |
376 | Total fees
377 |
378 |
379 | 0 DKK
380 |
381 |
382 |
383 |
384 |
385 |
390 | Total price
391 |
392 |
393 | {parsed
394 | ? numeral(parsed.interest).format()
395 | : "6,600"}{" "}
396 | USD
397 |
398 |
399 |
400 |
405 | Total cost
406 |
407 |
408 | {parsed
409 | ? numeral(parsed.cost).format()
410 | : "81,600"}{" "}
411 | USD
412 |
413 |
414 |
415 |
416 |
417 |
423 |
424 |
429 | How often
430 |
431 |
432 | Once a month
433 |
434 |
435 |
436 |
437 |
442 | When to start
443 |
444 |
445 | 01 February 2019
446 |
447 |
448 |
449 |
454 | When it ends?
455 |
456 |
457 | 01 May 2019
458 |
459 |
460 |
461 |
462 |
463 |
471 | Destination account
472 |
473 |
477 |
485 | }
486 | >
487 |
488 |
489 |
490 |
491 | Account one
492 |
493 |
494 | Account two
495 |
496 |
497 | Other account
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 | )}
506 | {activeStep === 3 && (
507 |
508 |
509 |
510 |
515 | Terms & Conditions
516 |
517 |
518 | Please read through and accept the terms &
519 | conditions
520 |
521 |
522 |
531 |
536 | 1. Your agreement
537 |
538 |
539 | By using this Site, you agree to be bound by, and to
540 | comply with, these Terms and Conditions. If you do
541 | not agree to these Terms and Conditions, please do
542 | not use this site. PLEASE NOTE: We reserve the
543 | right, at our sole discretion, to change, modify or
544 | otherwise alter these Terms and Conditions at any
545 | time. Unless otherwise indicated, amendments will
546 | become effective immediately. Please review these
547 | Terms and Conditions periodically. Your continued
548 | use of the Site following the posting of changes
549 | and/or modifications will constitute your acceptance
550 | of the revised Terms and Conditions and the
551 | reasonableness of these standards for notice of
552 | changes. For your information, this page was last
553 | updated as of the date at the top of these terms and
554 | conditions.
555 |
556 |
561 | 2. Privacy
562 |
563 |
564 | Please review our Privacy Policy, which also governs
565 | your visit to this Site, to understand our
566 | practices. By using this Site, you agree to be bound
567 | by, and to comply with, these Terms and Conditions.
568 | If you do not agree to these Terms and Conditions,
569 | please do not use this site. PLEASE NOTE: We reserve
570 | the right, at our sole discretion, to change, modify
571 | or otherwise alter these Terms and Conditions at any
572 | time. Unless otherwise indicated, amendments will
573 | become effective immediately. Please review these
574 | Terms and Conditions periodically. Your continued
575 | use of the Site following the posting of changes
576 | and/or modifications will constitute your acceptance
577 | of the revised Terms and Conditions and the
578 | reasonableness of these standards for notice of
579 | changes. For your information, this page was last
580 | updated as of the date at the top of these terms and
581 | conditions.
582 |
583 |
584 |
585 |
592 | }
593 | label="I have read and understood the terms & conditions"
594 | />
595 |
596 |
597 |
598 | )}
599 | {activeStep === 4 && (
600 |
601 |
602 |
603 |
604 |
609 | Sign & confirm
610 |
611 |
612 | Sign and confirm your agreement
613 |
614 |
615 |
616 |
617 |
618 | )}
619 | {(activeStep === 5 || activeStep === 6) && (
620 |
621 |
622 |
623 |
624 |
625 | Congratulations{" "}
626 |
627 | 🎉
628 |
629 |
630 |
631 | We have now a positive response
632 |
633 |
634 | Download the service invoice or whatever
635 |
636 |
637 |
638 |
639 |
640 | )}
641 |
642 | {activeStep !== 5 && (
643 |
649 | Back
650 |
651 | )}
652 |
663 | {this.stepActions()}
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 | );
673 | }
674 | }
675 |
676 | export default withRouter(withStyles(styles)(Wizard));
677 |
--------------------------------------------------------------------------------