Fixed selected nav item. Added lazy loading for WS client to resolve token issue. Added some filtering to jobs list.

This commit is contained in:
Patrick Fic
2019-12-12 12:23:31 -08:00
parent f6c62da40a
commit bca375251e
9 changed files with 266 additions and 42 deletions

View File

@@ -30,6 +30,7 @@ class AppContainer extends Component {
const wsLink = new WebSocketLink({ const wsLink = new WebSocketLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS, uri: process.env.REACT_APP_GRAPHQL_ENDPOINT_WS,
options: { options: {
lazy: true,
reconnect: true, reconnect: true,
connectionParams: () => { connectionParams: () => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
@@ -49,7 +50,11 @@ class AppContainer extends Component {
({ query }) => { ({ query }) => {
const definition = getMainDefinition(query); const definition = getMainDefinition(query);
console.log( console.log(
"##Intercepted GQL Transaction##" + definition.operation, "##Intercepted GQL Transaction : " +
definition.operation +
"|" +
definition.name.value +
"##",
query query
); );
return ( return (

View File

@@ -13,20 +13,17 @@ import SignInPage from "../pages/sign-in/sign-in.page";
import Unauthorized from "../pages/unauthorized/unauthorized.component"; import Unauthorized from "../pages/unauthorized/unauthorized.component";
import { auth } from "../firebase/firebase.utils"; import { auth } from "../firebase/firebase.utils";
import { UPSERT_USER } from "../graphql/user.queries";
import { GET_CURRENT_USER } from "../graphql/local.queries"; import { GET_CURRENT_USER } from "../graphql/local.queries";
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component"; import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
import AlertComponent from "../components/alert/alert.component"; import AlertComponent from "../components/alert/alert.component";
import SignOut from "../components/sign-out/sign-out.component";
import { UPSERT_USER } from "../graphql/user.queries";
export default () => { export default () => {
const apolloClient = useApolloClient(); const apolloClient = useApolloClient();
useEffect(() => { useEffect(() => {
//Run the auth code only on the first render. //Run the auth code only on the first render.
const unsubscribeFromAuth = auth.onAuthStateChanged(async user => { const unsubscribeFromAuth = auth.onIdTokenChanged(async user => {
if (user) { if (user) {
let token; let token;
token = await user.getIdToken(); token = await user.getIdToken();
@@ -85,7 +82,6 @@ export default () => {
return <AlertComponent message={HookCurrentUser.error.message} />; return <AlertComponent message={HookCurrentUser.error.message} />;
return ( return (
<div> <div>
<SignOut />
<Switch> <Switch>
<Route exact path="/" component={LandingPage} /> <Route exact path="/" component={LandingPage} />
<Route exact path="/unauthorized" component={Unauthorized} /> <Route exact path="/unauthorized" component={Unauthorized} />

View File

@@ -0,0 +1,80 @@
import React from "react";
import { Icon, Button, Input, AutoComplete } from "antd";
const { Option } = AutoComplete;
function onSelect(value) {
console.log("onSelect", value);
}
function getRandomInt(max, min = 0) {
return Math.floor(Math.random() * (max - min + 1)) + min; // eslint-disable-line no-mixed-operators
}
function searchResult(query) {
return new Array(getRandomInt(5))
.join(".")
.split(".")
.map((item, idx) => ({
query,
category: `${query}${idx}`,
count: getRandomInt(200, 100)
}));
}
function renderOption(item) {
return (
<Option key={item.category} text={item.category}>
<div className="global-search-item">
<span className="global-search-item-desc">
Found {item.query} on
<a
href={`https://s.taobao.com/search?q=${item.query}`}
target="_blank"
rel="noopener noreferrer"
>
{item.category}
</a>
</span>
<span className="global-search-item-count">{item.count} results</span>
</div>
</Option>
);
}
export default class GlobalSearch extends React.Component {
state = {
dataSource: []
};
handleSearch = value => {
this.setState({
dataSource: value ? searchResult(value) : []
});
};
render() {
const { dataSource } = this.state;
return (
<div style={{ width: 300 }}>
<AutoComplete
size="large"
style={{ width: "100%" }}
dataSource={dataSource.map(renderOption)}
onSelect={onSelect}
onSearch={this.handleSearch}
placeholder="input here"
optionLabelProp="text"
>
<Input
suffix={
<Button style={{ marginRight: -12 }} size="large" type="primary">
<Icon type="search" />
</Button>
}
/>
</AutoComplete>
</div>
);
}
}

View File

@@ -8,38 +8,56 @@ import {
GET_LANDING_NAV_ITEMS, GET_LANDING_NAV_ITEMS,
GET_NAV_ITEMS GET_NAV_ITEMS
} from "../../graphql/metadata.queries"; } from "../../graphql/metadata.queries";
import { GET_CURRENT_SELECTED_NAV_ITEM } from "../../graphql/local.queries";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import ManageSignInButton from "../manage-sign-in-button/manage-sign-in-button.component";
import { useApolloClient } from "@apollo/react-hooks";
import GlobalSearch from "../global-search/global-search.component";
export default ({ landingHeader }) => { export default ({ landingHeader, signedIn }) => {
let HookNavItems; const apolloClient = useApolloClient();
const hookSelectedNavItem = useQuery(GET_CURRENT_SELECTED_NAV_ITEM);
let hookNavItems;
if (landingHeader) { if (landingHeader) {
HookNavItems = useQuery(GET_LANDING_NAV_ITEMS); hookNavItems = useQuery(GET_LANDING_NAV_ITEMS, {
fetchPolicy: "network-only"
});
} else { } else {
HookNavItems = useQuery(GET_NAV_ITEMS); hookNavItems = useQuery(GET_NAV_ITEMS, {
fetchPolicy: "network-only"
});
} }
const handleClick = e => { const handleClick = e => {
console.log("click ", e); apolloClient.writeData({ data: { selectedNavItem: e.key } });
// this.setState({
// current: e.key
// });
}; };
if (HookNavItems.loading) return <LoadingSpinner />; if (hookNavItems.loading || hookSelectedNavItem.loading)
if (HookNavItems.error) return <LoadingSpinner />;
return <AlertComponent message={HookNavItems.error.message} />; if (hookNavItems.error)
return <AlertComponent message={hookNavItems.error.message} />;
if (hookSelectedNavItem.error)
return console.log(
"Unable to load Selected Navigation Item.",
hookSelectedNavItem.error
);
const { selectedNavItem } = hookSelectedNavItem.data;
const navItems = JSON.parse(hookNavItems.data.masterdata_by_pk.value);
const navItems = JSON.parse(HookNavItems.data.masterdata_by_pk.value);
return ( return (
<Menu <Menu
theme="dark" theme="dark"
className="header" className="header"
onClick={handleClick} onClick={handleClick}
selectedKeys="Home" selectedKeys={selectedNavItem}
mode="horizontal" mode="horizontal"
> >
<Menu.Item>
<GlobalSearch />
</Menu.Item>
{navItems.map(navItem => ( {navItems.map(navItem => (
<Menu.Item key={navItem.title}> <Menu.Item key={navItem.title}>
<Link to={navItem.path}> <Link to={navItem.path}>
@@ -48,11 +66,16 @@ export default ({ landingHeader }) => {
</Link> </Link>
</Menu.Item> </Menu.Item>
))} ))}
{!landingHeader ? ( {!landingHeader ? (
<Menu.Item> <Menu.Item>
<SignOut /> <SignOut />
</Menu.Item> </Menu.Item>
) : null} ) : (
<Menu.Item>
<ManageSignInButton />
</Menu.Item>
)}
</Menu> </Menu>
); );
}; };

View File

@@ -0,0 +1,33 @@
import React from "react";
import { useQuery } from "react-apollo";
import { Link } from "react-router-dom";
import { GET_CURRENT_USER } from "../../graphql/local.queries";
import { Icon } from "antd";
export default function ManageSignInButton() {
const {
loading,
error,
data: { currentUser }
} = useQuery(GET_CURRENT_USER);
if (loading) return "MSI Loading";
if (error) return error.message;
console.log("currentUser", currentUser);
return currentUser ? (
<div>
{" "}
<Link to="/manage">
<Icon type="build" />
Manage
</Link>
</div>
) : (
<div>
<Link to="/signin">
<Icon type="login" />
Sign In
</Link>
</div>
);
}

View File

@@ -9,7 +9,7 @@ export const SET_CURRENT_USER = gql`
`; `;
export const GET_CURRENT_USER = gql` export const GET_CURRENT_USER = gql`
{ query GET_CURRENT_USER {
currentUser @client { currentUser @client {
email email
displayName displayName
@@ -20,6 +20,12 @@ export const GET_CURRENT_USER = gql`
} }
`; `;
export const GET_CURRENT_SELECTED_NAV_ITEM = gql`
query GET_CURRENT_SELECTED_NAV_ITEM {
selectedNavItem @client
}
`;
export const GET_WHITE_BOARD_LEFT_SIDER_VISIBLE = gql` export const GET_WHITE_BOARD_LEFT_SIDER_VISIBLE = gql`
{ {
whiteBoardLeftSiderVisible @client whiteBoardLeftSiderVisible @client

View File

@@ -1,18 +1,16 @@
import React from "react"; import React, { useState } from "react";
import { useQuery, useSubscription } from "@apollo/react-hooks"; import { useSubscription } from "@apollo/react-hooks";
import AlertComponent from "../../components/alert/alert.component"; import AlertComponent from "../../components/alert/alert.component";
//import { GET_ALL_OPEN_JOBS } from "../../graphql/jobs.queries"; //import { GET_ALL_OPEN_JOBS } from "../../graphql/jobs.queries";
import { Table, Divider, Icon } from "antd"; import { Table, Divider, Icon } from "antd";
import { alphaSort } from "../../utils/sorters";
import { import { SUBSCRIPTION_ALL_OPEN_JOBS } from "../../graphql/jobs.queries";
GET_ALL_OPEN_JOBS, //import { columns } from "./jobs.page.metadata";
SUBSCRIPTION_ALL_OPEN_JOBS
} from "../../graphql/jobs.queries";
export default function JobsPage() { export default function JobsPage() {
// const { loading, error, data } = useQuery(GET_ALL_OPEN_JOBS, { const [sortedInfo, setSortedInfo] = useState({});
// fetchPolicy: "network-only"
// });
const { loading, error, data } = useSubscription(SUBSCRIPTION_ALL_OPEN_JOBS, { const { loading, error, data } = useSubscription(SUBSCRIPTION_ALL_OPEN_JOBS, {
fetchPolicy: "network-only" fetchPolicy: "network-only"
}); });
@@ -21,7 +19,10 @@ export default function JobsPage() {
{ {
title: "RO #", title: "RO #",
dataIndex: "ro_number", dataIndex: "ro_number",
key: "ro_number" key: "ro_number",
sorter: (a, b) => alphaSort(a, b),
sortOrder: sortedInfo.columnKey === "ro_number" && sortedInfo.order,
ellipsis: true
}, },
{ {
title: "Est. #", title: "Est. #",
@@ -31,7 +32,10 @@ export default function JobsPage() {
{ {
title: "Status", title: "Status",
dataIndex: "status", dataIndex: "status",
key: "status" key: "status",
sorter: (a, b) => alphaSort(a, b),
sortOrder: sortedInfo.columnKey === "status" && sortedInfo.order,
ellipsis: true
}, },
{ {
title: "Customer", title: "Customer",
@@ -67,22 +71,25 @@ export default function JobsPage() {
key: "action", key: "action",
render: (text, record) => ( render: (text, record) => (
<span> <span>
<a>Action {record.ro_number}</a> Action {record.ro_number}
<Divider type="vertical" /> <Divider type="vertical" />
<a>Delete</a>
<Divider type="vertical" /> <Divider type="vertical" />
<a className="ant-dropdown-link"> More actions <Icon type="down" />
More actions <Icon type="down" />
</a>
</span> </span>
) )
} }
]; ];
if (error) { const handleChange = (pagination, filters, sorter) => {
console.log("error", error); console.log("Various parameters", pagination, filters, sorter);
return <AlertComponent message={error} />; // this.setState({
} // filteredInfo: filters,
// sortedInfo: sorter,
// });
setSortedInfo(sorter);
};
if (error) return <AlertComponent message={error.message} />;
return ( return (
<div> <div>
@@ -92,6 +99,7 @@ export default function JobsPage() {
columns={columns.map(item => ({ ...item }))} columns={columns.map(item => ({ ...item }))}
rowKey="id" rowKey="id"
dataSource={data ? data.jobs : null} dataSource={data ? data.jobs : null}
onChange={handleChange}
/> />
</div> </div>
); );

View File

@@ -0,0 +1,64 @@
import React from "react";
import { Divider, Icon } from "antd";
export const columns = [
{
title: "RO #",
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) => a.ro_number > b.ro_number,
sortOrder: sortedInfo.columnKey === "ro_number" && sortedInfo.order,
ellipsis: true
},
{
title: "Est. #",
dataIndex: "est_number",
key: "est_number"
},
{
title: "Status",
dataIndex: "status",
key: "status"
},
{
title: "Customer",
dataIndex: "customer",
key: "customer",
render: (text, record) => {
return record.owner ? (
<div>
{record.owner.first_name} {record.owner.last_name}
</div>
) : (
"No Customer"
);
}
},
{
title: "Vehicle",
dataIndex: "vehicle",
key: "vehicle",
render: (text, record) => {
return record.vehicle ? (
<div>
{record.vehicle.v_model_yr} {record.vehicle.v_make_desc}{" "}
{record.vehicle.v_model_desc}
</div>
) : (
"No Vehicle"
);
}
},
{
title: "Action",
key: "action",
render: (text, record) => (
<span>
Action {record.ro_number}
<Divider type="vertical" />
<Divider type="vertical" />
More actions <Icon type="down" />
</span>
)
}
];

View File

@@ -0,0 +1,9 @@
export function alphaSort(a, b) {
if (a > b) {
return false;
}
if (b > a) {
return true;
}
return true;
}