Fixed up general layout of manage page + root + styled login page.

This commit is contained in:
Patrick Fic
2020-06-10 22:07:03 -07:00
parent afbec7d79e
commit cec3fec481
14 changed files with 269 additions and 105 deletions

View File

@@ -5081,6 +5081,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>login</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>refresh</name> <name>refresh</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -5548,6 +5569,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>password</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>search</name> <name>search</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -5632,6 +5674,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>username</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>yes</name> <name>yes</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -1,32 +1,36 @@
import React from "react"; import { HomeFilled } from "@ant-design/icons";
import { Breadcrumb } from "antd"; import { Breadcrumb } from "antd";
import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBreadcrumbs } from "../../redux/application/application.selectors"; import { selectBreadcrumbs } from "../../redux/application/application.selectors";
import { Link } from "react-router-dom"; import "./breadcrumbs.styles.scss";
import { HomeFilled } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
breadcrumbs: selectBreadcrumbs, breadcrumbs: selectBreadcrumbs,
}); });
export function BreadCrumbs({ breadcrumbs }) { export function BreadCrumbs({ breadcrumbs }) {
return ( return (
<Breadcrumb> <div className='breadcrumb-container'>
<Breadcrumb.Item> <Breadcrumb separator='>'>
<Link to={`/manage`}> <Breadcrumb.Item>
<HomeFilled /> <Link to={`/manage`}>
</Link> <HomeFilled />
</Breadcrumb.Item> </Link>
{breadcrumbs.map((item) => </Breadcrumb.Item>
item.link ? ( {breadcrumbs.map((item) =>
<Breadcrumb.Item key={item.label}> item.link ? (
<Link to={item.link}>{item.label} </Link> <Breadcrumb.Item key={item.label}>
</Breadcrumb.Item> <Link to={item.link}>{item.label} </Link>
) : ( </Breadcrumb.Item>
<Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item> ) : (
) <Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item>
)} )
</Breadcrumb> )}
</Breadcrumb>
</div>
); );
} }
export default connect(mapStateToProps, null)(BreadCrumbs); export default connect(mapStateToProps, null)(BreadCrumbs);

View File

@@ -0,0 +1,3 @@
.breadcrumb-container {
margin: 1rem 4rem;
}

View File

@@ -6,21 +6,22 @@ import Icon, {
GlobalOutlined, GlobalOutlined,
HomeFilled, HomeFilled,
TeamOutlined, TeamOutlined,
UserOutlined
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Avatar, Col, Menu, Row, Layout } from "antd"; import { Avatar, Col, Layout, Menu, Row } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { FaCalendarAlt, FaCarCrash } from "react-icons/fa"; import { FaCalendarAlt, FaCarCrash } from "react-icons/fa";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import UserImage from "../../assets/User.svg";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { signOutStart } from "../../redux/user/user.actions"; import { signOutStart } from "../../redux/user/user.actions";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import "./header.styles.scss";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
@@ -37,6 +38,32 @@ const mapDispatchToProps = (dispatch) => ({
signOutStart: () => dispatch(signOutStart()), signOutStart: () => dispatch(signOutStart()),
}); });
const logoSpan = {
xs: {
span: 0,
},
md: {
span: 1,
},
lg: {
span: 2,
},
};
const menuSpan = {
xs: {
span: 24,
},
md: {
span: 22,
offset: 1,
},
lg: {
span: 21,
offset: 1,
},
};
function Header({ function Header({
bodyshop, bodyshop,
handleMenuClick, handleMenuClick,
@@ -47,12 +74,14 @@ function Header({
setPaymentContext, setPaymentContext,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { Header } = Layout;
return ( return (
<Layout.Header> <Header>
<Row align='middle'> <Row>
<Col span={4}> <Col {...logoSpan}>
<img <img
className='header-shop-logo'
alt={bodyshop ? bodyshop.shopname : "ImEX Online Logo"} alt={bodyshop ? bodyshop.shopname : "ImEX Online Logo"}
src={ src={
bodyshop && bodyshop.logo_img_path bodyshop && bodyshop.logo_img_path
@@ -61,8 +90,12 @@ function Header({
} }
/> />
</Col> </Col>
<Col span={16}> <Col {...menuSpan}>
<Menu mode='horizontal' onClick={handleMenuClick}> <Menu
mode='horizontal'
theme='dark'
className='header-main-menu'
onClick={handleMenuClick}>
<Menu.Item key='home'> <Menu.Item key='home'>
<Link to='/manage'> <Link to='/manage'>
<HomeFilled /> <HomeFilled />
@@ -114,7 +147,6 @@ function Header({
</Link> </Link>
</Menu.Item> </Menu.Item>
</Menu.SubMenu> </Menu.SubMenu>
<Menu.SubMenu <Menu.SubMenu
title={ title={
<span> <span>
@@ -141,7 +173,6 @@ function Header({
</Link> </Link>
</Menu.Item> </Menu.Item>
</Menu.SubMenu> </Menu.SubMenu>
<Menu.SubMenu <Menu.SubMenu
title={ title={
<span> <span>
@@ -194,7 +225,6 @@ function Header({
</Link> </Link>
</Menu.Item> </Menu.Item>
</Menu.SubMenu> </Menu.SubMenu>
<Menu.SubMenu title={t("menus.header.shop")}> <Menu.SubMenu title={t("menus.header.shop")}>
<Menu.Item key='shop'> <Menu.Item key='shop'>
<Link to='/manage/shop'>{t("menus.header.shop_config")}</Link> <Link to='/manage/shop'>{t("menus.header.shop_config")}</Link>
@@ -213,21 +243,26 @@ function Header({
<Link to='/manage/shop/csi'>{t("menus.header.shop_csi")}</Link> <Link to='/manage/shop/csi'>{t("menus.header.shop_csi")}</Link>
</Menu.Item> </Menu.Item>
</Menu.SubMenu> </Menu.SubMenu>
</Menu>
</Col>
<Col span={4}>
<Menu>
<Menu.SubMenu <Menu.SubMenu
title={ title={
<div> <div>
<Avatar {currentUser.photoURL ? (
size='medium' <Avatar
alt='Avatar' src={currentUser.photoURL}
src={ style={{
currentUser.photoURL ? currentUser.photoURL : UserImage margin: "10px",
} }}
style={{ margin: "10px" }} />
/> ) : (
<Avatar
style={{
backgroundColor: "#87d068",
margin: "10px",
}}
icon={<UserOutlined />}
/>
)}
{currentUser.displayName || t("general.labels.unknown")} {currentUser.displayName || t("general.labels.unknown")}
</div> </div>
}> }>
@@ -260,7 +295,7 @@ function Header({
</Menu> </Menu>
</Col> </Col>
</Row> </Row>
</Layout.Header> </Header>
); );
} }

View File

@@ -0,0 +1,9 @@
.header-shop-logo {
background-size: cover;
max-width: 100%;
max-height: 3.5rem;
}
.header-main-menu {
width: 80vw;
float: left;
}

View File

@@ -1,4 +1,4 @@
import { useElements, useStripe, CardElement } from "@stripe/react-stripe-js"; import { useElements, useStripe } from "@stripe/react-stripe-js";
import { Form, Modal } from "antd"; import { Form, Modal } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -11,7 +11,6 @@ import {
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import PaymentForm from "../payment-form/payment-form.container"; import PaymentForm from "../payment-form/payment-form.container";
import axios from "axios";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
paymentModal: selectPayment, paymentModal: selectPayment,
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -38,7 +37,7 @@ function InvoiceEnterModalContainer({
error: null, error: null,
cardComplete: false, cardComplete: false,
}); });
const [stripeState, setStripeState] = stripeStateArr; const stripeState = stripeStateArr[0];
const cardValid = !!!stripeState.error && stripeState.cardComplete; const cardValid = !!!stripeState.error && stripeState.cardComplete;
@@ -105,14 +104,12 @@ function InvoiceEnterModalContainer({
onCancel={handleCancel} onCancel={handleCancel}
afterClose={() => form.resetFields()} afterClose={() => form.resetFields()}
okButtonProps={{ loading: loading, disabled: !cardValid }} okButtonProps={{ loading: loading, disabled: !cardValid }}
destroyOnClose destroyOnClose>
>
<Form <Form
onFinish={handleFinish} onFinish={handleFinish}
autoComplete={"off"} autoComplete={"off"}
form={form} form={form}
initialValues={{ jobid: context.jobId }} initialValues={{ jobid: context.jobId }}>
>
<PaymentForm form={form} stripeStateArr={stripeStateArr} /> <PaymentForm form={form} stripeStateArr={stripeStateArr} />
</Form> </Form>
</Modal> </Modal>

View File

@@ -1,17 +1,20 @@
import { LockOutlined, UserOutlined } from "@ant-design/icons"; import { LockOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Form, Input } from "antd"; import { Button, Form, Input, Typography } from "antd";
import React from "react"; import React from "react";
import { useApolloClient } from "react-apollo"; import { useApolloClient } from "react-apollo";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Redirect } from "react-router-dom"; import { Redirect } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import Logo from "../../assets/logo240.png"; import ImEXOnlineLogo from "../../assets/logo240.png";
import { UPSERT_USER } from "../../graphql/user.queries"; import { UPSERT_USER } from "../../graphql/user.queries";
import { emailSignInStart } from "../../redux/user/user.actions"; import { emailSignInStart } from "../../redux/user/user.actions";
import { import {
selectCurrentUser, selectCurrentUser,
selectSignInError, selectSignInError,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import { useTranslation } from "react-i18next";
import "./sign-in-form.styles.scss";
import AlertComponent from "../alert/alert.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
@@ -23,16 +26,18 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(emailSignInStart({ email, password })), dispatch(emailSignInStart({ email, password })),
}); });
export default connect( export function SignInComponent({
mapStateToProps, emailSignInStart,
mapDispatchToProps currentUser,
)(function SignInComponent({ emailSignInStart, currentUser, signInError }) { signInError,
}) {
const apolloClient = useApolloClient(); const apolloClient = useApolloClient();
const { t } = useTranslation();
const handleFinish = (values) => { const handleFinish = (values) => {
const { email, password } = values; const { email, password } = values;
emailSignInStart(email, password); emailSignInStart(email, password);
}; };
const [form] = Form.useForm();
if (currentUser.authorized === true) { if (currentUser.authorized === true) {
apolloClient apolloClient
@@ -49,38 +54,48 @@ export default connect(
}); });
} }
const handleLogin = () => {
form.submit();
};
if (currentUser.authorized === true) return <Redirect to='/manage' />;
return ( return (
<div style={{ width: "450px" }}> <div className='login-container'>
{currentUser.authorized === true ? <Redirect to="/manage?" /> : null} <div className='login-logo-container'>
<img src={ImEXOnlineLogo} height='100' width='100' alt='ImEX Online' />
<img src={Logo} height="100" width="100" alt="Bodyshop.app" /> <Typography.Title>{t("titles.app")}</Typography.Title>
</div>
<Form onFinish={handleFinish}> <Form onFinish={handleFinish} form={form} size='large'>
<Form.Item <Form.Item
name="email" name='email'
rules={[{ required: true, message: "Please input your email!" }]} rules={[
> { required: true, message: t("general.validation.required") },
]}>
<Input <Input
prefix={<UserOutlined className="site-form-item-icon" />} prefix={<UserOutlined />}
placeholder="Username" placeholder={t("general.labels.username")}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="password" name='password'
rules={[{ required: true, message: "Please input your Password!" }]} rules={[
> { required: true, message: t("general.validation.required") },
]}>
<Input <Input
prefix={<LockOutlined className="site-form-item-icon" />} prefix={<LockOutlined />}
type="password" type='password'
placeholder="Password" placeholder={t("general.labels.password")}
/> />
</Form.Item> </Form.Item>
<Button type="primary" htmlType="submit"> {signInError ? (
Log in <AlertComponent type='error' message={signInError.message} />
) : null}
<Button className='login-btn' type='primary' htmlType='submit'>
{t("general.actions.login")}
</Button> </Button>
{signInError ? <div>{signInError.message}</div> : null}
</Form> </Form>
</div> </div>
); );
}); }
export default connect(mapStateToProps, mapDispatchToProps)(SignInComponent);

View File

@@ -0,0 +1,28 @@
.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);
}

View File

@@ -1,10 +1,12 @@
import { Elements } from "@stripe/react-stripe-js"; import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
import { BackTop, Layout } from "antd"; import { BackTop, Layout } from "antd";
import React, { lazy, Suspense, useEffect } from "react"; import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Route, Switch } from "react-router-dom"; import { Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { client } from "../../App/App.container";
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component"; import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container"; import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
import ConflictComponent from "../../components/conflict/conflict.component"; import ConflictComponent from "../../components/conflict/conflict.component";
@@ -15,14 +17,9 @@ import FooterComponent from "../../components/footer/footer.component";
import HeaderContainer from "../../components/header/header.container"; import HeaderContainer from "../../components/header/header.container";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container"; import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
import {
selectInstanceConflict,
selectBodyshop,
} from "../../redux/user/user.selectors";
import "./manage.page.styles.scss";
import { loadStripe } from "@stripe/stripe-js";
import { client } from "../../App/App.container";
import { QUERY_STRIPE_ID } from "../../graphql/bodyshop.queries"; import { QUERY_STRIPE_ID } from "../../graphql/bodyshop.queries";
import { selectInstanceConflict } from "../../redux/user/user.selectors";
import "./manage.page.styles.scss";
const ManageRootPage = lazy(() => const ManageRootPage = lazy(() =>
import("../manage-root/manage-root.page.container") import("../manage-root/manage-root.page.container")
@@ -113,7 +110,7 @@ const ShopCsiPageContainer = lazy(() =>
import("../shop-csi/shop-csi.container.page") import("../shop-csi/shop-csi.container.page")
); );
const { Header, Content, Footer } = Layout; const { Content, Footer } = Layout;
const stripePromise = new Promise((resolve, reject) => { const stripePromise = new Promise((resolve, reject) => {
client.query({ query: QUERY_STRIPE_ID }).then((resp) => { client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
@@ -137,10 +134,11 @@ export function Manage({ match, conflict }) {
}, [t]); }, [t]);
return ( return (
<Layout style={{ minHeight: "100vh" }}> <Layout className='layout-container'>
<HeaderContainer /> <HeaderContainer />
<BreadCrumbs />
<Content className='content-container' style={{ padding: "0em 4em 4em" }}> <Content className='content-container'>
<FcmNotification /> <FcmNotification />
<ErrorBoundary> <ErrorBoundary>
{conflict ? ( {conflict ? (
@@ -150,7 +148,6 @@ export function Manage({ match, conflict }) {
fallback={ fallback={
<LoadingSpinner message={t("general.labels.loadingapp")} /> <LoadingSpinner message={t("general.labels.loadingapp")} />
}> }>
<BreadCrumbs />
<EnterInvoiceModalContainer /> <EnterInvoiceModalContainer />
<EmailOverlayContainer /> <EmailOverlayContainer />
<TimeTicketModalContainer /> <TimeTicketModalContainer />
@@ -302,13 +299,15 @@ export function Manage({ match, conflict }) {
</Suspense> </Suspense>
)} )}
</ErrorBoundary> </ErrorBoundary>
<ChatAffixContainer />
<BackTop />
</Content> </Content>
<Footer> {
<FooterComponent /> // <Footer>
</Footer> // <FooterComponent />
<ChatAffixContainer /> // </Footer>
<BackTop /> }
</Layout> </Layout>
); );
} }

View File

@@ -1 +1,10 @@
.content-container { overflow-y : scroll; } .content-container {
overflow-y: auto;
margin: 0.5rem 1.5rem;
padding: 1.5rem;
background: #fff;
}
.layout-container {
height: 100vh;
}

View File

@@ -3,14 +3,7 @@ import SignIn from "../../components/sign-in-form/sign-in-form.component";
export default () => { export default () => {
return ( return (
<div <div>
style={{
display: "flex",
justifyContent: "center",
alignItems: "middle",
padding: "4em",
}}
>
<SignIn /> <SignIn />
</div> </div>
); );

View File

@@ -345,6 +345,7 @@
"create": "Create", "create": "Create",
"delete": "Delete", "delete": "Delete",
"edit": "Edit", "edit": "Edit",
"login": "Login",
"refresh": "Refresh", "refresh": "Refresh",
"reset": "Reset to original.", "reset": "Reset to original.",
"save": "Save", "save": "Save",
@@ -365,14 +366,16 @@
"loading": "Loading...", "loading": "Loading...",
"loadingapp": "Loading Bodyshop.app", "loadingapp": "Loading Bodyshop.app",
"loadingshop": "Loading shop data...", "loadingshop": "Loading shop data...",
"loggingin": "Logging you in...", "loggingin": "Authorizing...",
"na": "N/A", "na": "N/A",
"no": "No", "no": "No",
"out": "Out", "out": "Out",
"password": "Password",
"search": "Search...", "search": "Search...",
"selectdate": "Select date...", "selectdate": "Select date...",
"text": "Text", "text": "Text",
"unknown": "Unknown", "unknown": "Unknown",
"username": "Username",
"yes": "Yes" "yes": "Yes"
}, },
"languages": { "languages": {

View File

@@ -345,6 +345,7 @@
"create": "", "create": "",
"delete": "Borrar", "delete": "Borrar",
"edit": "Editar", "edit": "Editar",
"login": "",
"refresh": "", "refresh": "",
"reset": "Restablecer a original.", "reset": "Restablecer a original.",
"save": "Salvar", "save": "Salvar",
@@ -369,10 +370,12 @@
"na": "N / A", "na": "N / A",
"no": "", "no": "",
"out": "Afuera", "out": "Afuera",
"password": "",
"search": "Buscar...", "search": "Buscar...",
"selectdate": "", "selectdate": "",
"text": "", "text": "",
"unknown": "Desconocido", "unknown": "Desconocido",
"username": "",
"yes": "" "yes": ""
}, },
"languages": { "languages": {

View File

@@ -345,6 +345,7 @@
"create": "", "create": "",
"delete": "Effacer", "delete": "Effacer",
"edit": "modifier", "edit": "modifier",
"login": "",
"refresh": "", "refresh": "",
"reset": "Rétablir l'original.", "reset": "Rétablir l'original.",
"save": "sauvegarder", "save": "sauvegarder",
@@ -369,10 +370,12 @@
"na": "N / A", "na": "N / A",
"no": "", "no": "",
"out": "En dehors", "out": "En dehors",
"password": "",
"search": "Chercher...", "search": "Chercher...",
"selectdate": "", "selectdate": "",
"text": "", "text": "",
"unknown": "Inconnu", "unknown": "Inconnu",
"username": "",
"yes": "" "yes": ""
}, },
"languages": { "languages": {