Initial database structure and migrations, login flow, refactor main page.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { Menu } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { signOutStart } from "../../../redux/user/user.actions";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
signOutStart: () => dispatch(signOutStart()),
|
||||
});
|
||||
|
||||
export function SiderSignOut({ signOutStart, ...restProps }) {
|
||||
return (
|
||||
<Menu.Item {...restProps} onClick={() => signOutStart()}>
|
||||
Sign Out
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(SiderSignOut);
|
||||
37
src/components/organisms/sider-menu/sider-menu.organism.jsx
Normal file
37
src/components/organisms/sider-menu/sider-menu.organism.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
DesktopOutlined,
|
||||
FileOutlined,
|
||||
PieChartOutlined,
|
||||
TeamOutlined,
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Menu } from "antd";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import SiderSignOut from "../../molecules/sider-sign-out/sider-sign-out.molecule";
|
||||
|
||||
const { SubMenu } = Menu;
|
||||
|
||||
export default function SiderMenuOrganism() {
|
||||
return (
|
||||
<Menu defaultSelectedKeys={["1"]} mode="inline">
|
||||
<Menu.Item key="1" icon={<PieChartOutlined />}>
|
||||
<Link to="/">Jobs</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2" icon={<DesktopOutlined />}>
|
||||
<Link to="/nope">Jobs Not Working</Link>
|
||||
</Menu.Item>
|
||||
<SubMenu key="sub1" icon={<UserOutlined />} title="User">
|
||||
<Menu.Item key="3">Tom</Menu.Item>
|
||||
<Menu.Item key="4">Bill</Menu.Item>
|
||||
<Menu.Item key="5">Alex</Menu.Item>
|
||||
</SubMenu>
|
||||
<SubMenu key="sub2" icon={<TeamOutlined />} title="Team">
|
||||
<Menu.Item key="6">Team 1</Menu.Item>
|
||||
<Menu.Item key="8">Team 2</Menu.Item>
|
||||
</SubMenu>
|
||||
<Menu.Item key="9" icon={<FileOutlined />} />
|
||||
<SiderSignOut />
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
50
src/components/pages/jobs/jobs.page.jsx
Normal file
50
src/components/pages/jobs/jobs.page.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Button } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
//const settings = window.require("electron-settings");
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export function JobsPage() {
|
||||
useEffect(() => {
|
||||
ipcRenderer.on("test-success", (event, obj) => {
|
||||
console.log("Test Success", obj);
|
||||
});
|
||||
ipcRenderer.on(ipcTypes.default.filewatcher.startSuccess, (event, obj) => {
|
||||
console.log(ipcTypes.default.filewatcher.startSuccess, obj);
|
||||
});
|
||||
// Cleanup the listener events so that memory leaks are avoided.
|
||||
return function cleanup() {
|
||||
ipcRenderer.removeAllListeners(
|
||||
"test-success",
|
||||
ipcTypes.default.filewatcher.startSuccess
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Welcome to your new react app. asdas sd</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
ipcRenderer.send("test", { test: true });
|
||||
}}
|
||||
>
|
||||
TEST Generic IPC
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
ipcRenderer.send(ipcTypes.default.filewatcher.start);
|
||||
}}
|
||||
>
|
||||
Start Watcher
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsPage);
|
||||
28
src/components/pages/routes/routes.page.jsx
Normal file
28
src/components/pages/routes/routes.page.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Layout } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import SiderMenuOrganism from "../../organisms/sider-menu/sider-menu.organism";
|
||||
import Jobs from "../jobs/jobs.page";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export function RoutesPage() {
|
||||
return (
|
||||
<Layout style={{ background: "#fff" }} hasSider>
|
||||
<Layout.Sider style={{ background: "#fff" }} collapsible>
|
||||
<SiderMenuOrganism />
|
||||
</Layout.Sider>
|
||||
<Layout style={{ background: "#fff" }}>
|
||||
<Layout.Content style={{ margin: "1rem" }}>
|
||||
<Switch>
|
||||
<Route exact path="/" component={Jobs} />
|
||||
</Switch>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(RoutesPage);
|
||||
59
src/components/pages/sign-in/sign-in.page.jsx
Normal file
59
src/components/pages/sign-in/sign-in.page.jsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { LockOutlined, UserOutlined } from "@ant-design/icons";
|
||||
import { Alert, Button, Form, Input, Typography } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ImEXOnlineLogo from "../../../assets/logo192.png";
|
||||
import { emailSignInStart } from "../../../redux/user/user.actions";
|
||||
import { selectSignInError } from "../../../redux/user/user.selectors";
|
||||
import "./sign-in.page.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
signInError: selectSignInError,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
emailSignInStart: (email, password) =>
|
||||
dispatch(emailSignInStart({ email, password })),
|
||||
});
|
||||
|
||||
export function SignInPage({ emailSignInStart, signInError }) {
|
||||
const handleFinish = (values) => {
|
||||
const { email, password } = values;
|
||||
emailSignInStart(email, password);
|
||||
};
|
||||
const [form] = Form.useForm();
|
||||
|
||||
return (
|
||||
<div className="login-container">
|
||||
<div className="login-logo-container">
|
||||
<img src={ImEXOnlineLogo} height="100" width="100" alt="ImEX Online" />
|
||||
<Typography.Title>ImEX RPS</Typography.Title>
|
||||
</div>
|
||||
<Form onFinish={handleFinish} form={form} size="large">
|
||||
<Form.Item name="email" rules={[{ required: true }]}>
|
||||
<Input prefix={<UserOutlined />} placeholder="Email" />
|
||||
</Form.Item>
|
||||
<Form.Item name="password" rules={[{ required: true }]}>
|
||||
<Input
|
||||
prefix={<LockOutlined />}
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
/>
|
||||
</Form.Item>
|
||||
{signInError ? (
|
||||
<Alert type="error" message={signInError.message} />
|
||||
) : null}
|
||||
<Button className="login-btn" type="primary" htmlType="submit">
|
||||
Login
|
||||
</Button>
|
||||
</Form>
|
||||
<Link to={"/resetpassword"}>
|
||||
<Button>Reset Password</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SignInPage);
|
||||
29
src/components/pages/sign-in/sign-in.page.styles.scss
Normal file
29
src/components/pages/sign-in/sign-in.page.styles.scss
Normal file
@@ -0,0 +1,29 @@
|
||||
.login-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
padding: 2rem;
|
||||
form {
|
||||
width: 75vw;
|
||||
max-width: 20rem;
|
||||
}
|
||||
}
|
||||
|
||||
.login-logo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
//Required as it is position inside form.
|
||||
.login-btn {
|
||||
margin: 1.5rem 0rem;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
64
src/components/templates/error-boundary.template.jsx
Normal file
64
src/components/templates/error-boundary.template.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Button, Col, Collapse, Result, Row, Space } from "antd";
|
||||
import React from "react";
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
hasErrored: false,
|
||||
error: null,
|
||||
info: null,
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
console.log("ErrorBoundary -> getDerivedStateFromError -> error", error);
|
||||
return { hasErrored: true, error: error };
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
console.log("Exception Caught by Error Boundary.", error, info);
|
||||
this.setState({ ...this.state, error, info });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasErrored === true) {
|
||||
return (
|
||||
<div>
|
||||
<Result
|
||||
status="500"
|
||||
title="Error!"
|
||||
subTitle="Error subtitle"
|
||||
extra={
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
window.location.reload();
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
<Row>
|
||||
<Col offset={6} span={12}>
|
||||
<Collapse bordered={false}>
|
||||
<Collapse.Panel header="Error Details">
|
||||
<div>
|
||||
<strong>{this.state.error.message}</strong>
|
||||
</div>
|
||||
<div>{this.state.error.stack}</div>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
}
|
||||
export default ErrorBoundary;
|
||||
55
src/components/test.jsx
Normal file
55
src/components/test.jsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Button, Layout } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../ipc.types";
|
||||
|
||||
const { ipcRenderer } = window.require("electron");
|
||||
//const settings = window.require("electron-settings");
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export function App() {
|
||||
useEffect(() => {
|
||||
ipcRenderer.on("test-success", (event, obj) => {
|
||||
console.log("Test Success", obj);
|
||||
});
|
||||
ipcRenderer.on(ipcTypes.default.filewatcher.startSuccess, (event, obj) => {
|
||||
console.log(ipcTypes.default.filewatcher.startSuccess, obj);
|
||||
});
|
||||
// Cleanup the listener events so that memory leaks are avoided.
|
||||
return function cleanup() {
|
||||
ipcRenderer.removeAllListeners(
|
||||
"test-success",
|
||||
ipcTypes.default.filewatcher.startSuccess
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout.Header>
|
||||
<div> Header</div>
|
||||
</Layout.Header>
|
||||
<Layout.Content>
|
||||
<div>Welcome to your new react app. asdas sd</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
ipcRenderer.send("test", { test: true });
|
||||
}}
|
||||
>
|
||||
TEST Generic IPC
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
ipcRenderer.send(ipcTypes.default.filewatcher.start);
|
||||
}}
|
||||
>
|
||||
Start Watcher
|
||||
</Button>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
||||
Reference in New Issue
Block a user