diff --git a/client/src/App/App.container.jsx b/client/src/App/App.container.jsx index 0c56c2748..15eb73548 100644 --- a/client/src/App/App.container.jsx +++ b/client/src/App/App.container.jsx @@ -16,7 +16,7 @@ import { ApolloLink } from "apollo-boost"; import { ApolloProvider } from "react-apollo"; import { persistCache } from "apollo-cache-persist"; import initialState from "../graphql/initial-state"; -import { shouldRefreshToken, refreshToken } from "../graphql/middleware"; +//import { shouldRefreshToken, refreshToken } from "../graphql/middleware"; import errorLink from "../graphql/apollo-error-handling"; class AppContainer extends Component { diff --git a/client/src/assets/User.svg b/client/src/assets/User.svg new file mode 100644 index 000000000..0ff014df1 --- /dev/null +++ b/client/src/assets/User.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/client/src/components/current-user-dropdown/current-user-dropdown.component.jsx b/client/src/components/current-user-dropdown/current-user-dropdown.component.jsx new file mode 100644 index 000000000..b4d4af3d7 --- /dev/null +++ b/client/src/components/current-user-dropdown/current-user-dropdown.component.jsx @@ -0,0 +1,69 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { Dropdown, Menu, Icon, Avatar, Typography, Row, Col } from "antd"; +import { useTranslation } from "react-i18next"; +import i18next from "i18next"; +import { useQuery } from "@apollo/react-hooks"; +import { GET_CURRENT_USER } from "../../graphql/local.queries"; +import UserImage from "../../assets/User.svg"; +import AlertComponent from "../alert/alert.component"; + +export default function CurrentUserDropdown() { + const { t } = useTranslation(); + const { loading, error, data } = useQuery(GET_CURRENT_USER); + + const handleMenuClick = e => { + console.log("e", e); + if (e.item.props.actiontype === "lang-select") { + i18next.changeLanguage(e.key, (err, t) => { + if (err) + return console.log("Error encountered when changing languages.", err); + }); + } + }; + const menu = ( + + + {t("menus.currentuser.profile")} + + + + {t("menus.currentuser.languageselector")} + + } + > + + {t("general.languages.english")} + + + {t("general.languages.french")} + + + {t("general.languages.spanish")} + + + + ); + + if (loading) return null; + if (error) return ; + + const { currentUser } = data; + + return ( + + + + + + + + {currentUser?.displayName ?? t("general.labels.unknown")} + + + + + ); +} diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx index 9d26d0de5..052543a23 100644 --- a/client/src/components/header/header.component.jsx +++ b/client/src/components/header/header.component.jsx @@ -1,51 +1,56 @@ import React from "react"; import { Link } from "react-router-dom"; import { useApolloClient } from "@apollo/react-hooks"; -import { Menu, Icon } from "antd"; +import { Menu, Icon, Row, Col } from "antd"; import "./header.styles.scss"; import SignOut from "../sign-out/sign-out.component"; import ManageSignInButton from "../manage-sign-in-button/manage-sign-in-button.component"; import GlobalSearch from "../global-search/global-search.component"; -import LanguageSelector from "../language-selector/langauge-selector.component"; +import CurrentUserDropdown from "../current-user-dropdown/current-user-dropdown.component"; export default ({ landingHeader, navItems, selectedNavItem }) => { const apolloClient = useApolloClient(); - const handleClick = e => { apolloClient.writeData({ data: { selectedNavItem: e.key } }); }; return ( - - - - - {navItems.map(navItem => ( - - - {navItem.icontype ? : null} - {navItem.title} - - - ))} + + + + + + + {navItems.map(navItem => ( + + + {navItem.icontype ? : null} + {navItem.title} + + + ))} - {!landingHeader ? ( - - - - ) : ( - - - - )} - - {!landingHeader ? : null} - + {!landingHeader ? ( + + + + ) : ( + + + + )} + + + + {!landingHeader ? : null} + + ); }; diff --git a/client/src/components/language-selector/langauge-selector.component.jsx b/client/src/components/language-selector/langauge-selector.component.jsx index 139f3580e..177c51d4f 100644 --- a/client/src/components/language-selector/langauge-selector.component.jsx +++ b/client/src/components/language-selector/langauge-selector.component.jsx @@ -7,8 +7,6 @@ export default function LanguageSelector() { const { t } = useTranslation(); const handleMenuClick = e => { - console.log("e", e); - i18next.changeLanguage(e.key, (err, t) => { if (err) return console.log("Error encountered when changing languages.", err); @@ -16,14 +14,14 @@ export default function LanguageSelector() { }; const menu = ( - {t("general.languages.english")} - {t("general.languages.french")} - {t("general.languages.spanish")} + {t("general.languages.english")} + {t("general.languages.french")} + {t("general.languages.spanish")} ); return ( - + ); } diff --git a/client/src/components/profile-content/profile-content.component.jsx b/client/src/components/profile-content/profile-content.component.jsx new file mode 100644 index 000000000..f570c99c0 --- /dev/null +++ b/client/src/components/profile-content/profile-content.component.jsx @@ -0,0 +1,18 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import AlertComponent from "../alert/alert.component"; + +export default function ProfileContent({ sidebarSelection }) { + const { t } = useTranslation(); + + switch (sidebarSelection.key) { + case "profile": + return
Profile stuff
; + case "shop": + return
Shop stuff
; + default: + return ( + + ); + } +} diff --git a/client/src/components/profile-sidebar/profile-sidebar.component.jsx b/client/src/components/profile-sidebar/profile-sidebar.component.jsx new file mode 100644 index 000000000..b029bfb75 --- /dev/null +++ b/client/src/components/profile-sidebar/profile-sidebar.component.jsx @@ -0,0 +1,34 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Layout, Menu, Icon } from "antd"; + +export default function ProfileSideBar({ + sidebarSelection, + setSidebarSelection +}) { + const { t } = useTranslation(); + + const onMenuClick = e => { + setSidebarSelection({ ...sidebarSelection, key: e.key }); + }; + + return ( + + + + + {t("menus.profilesidebar.profile")} + + + + {t("menus.profilesidebar.shops")} + + + + ); +} diff --git a/client/src/components/white-board-card/white-board-card.component.jsx b/client/src/components/white-board-card/white-board-card.component.jsx index 9f89797a2..9c0a6fa42 100644 --- a/client/src/components/white-board-card/white-board-card.component.jsx +++ b/client/src/components/white-board-card/white-board-card.component.jsx @@ -31,28 +31,28 @@ export default function WhiteBoardCard({ metadata }) { const menu = ( - - + + {t("whiteboard.viewJobImages")} - - + + {t("whiteboard.printCenter")} - - + + {t("whiteboard.notes")} - - + + {t("whiteboard.postInvoices")} - - + + {t("whiteboard.receiveParts")} - - + + {t("whiteboard.partStatus")} @@ -72,28 +72,31 @@ export default function WhiteBoardCard({ metadata }) { bodyStyle={{ padding: 10 }} actions={[ - + , - , + , - + - ]}> + ]} + > - + - {`${metadata.vehicle?.v_model_yr ?? "N/A"} ${metadata.vehicle - ?.v_make_desc ?? "N/A"} ${metadata.vehicle?.v_model_desc ?? - "N/A"}`} + {metadata.vehicle?.v_model_yr ?? t("general.labels.na")}{" "} + {metadata.vehicle?.v_make_desc ?? t("general.labels.na")}{" "} + {metadata.vehicle?.v_model_desc ?? t("general.labels.na")} {metadata.vehicle?.v_vin ? ( - {`VIN: ${metadata.vehicle?.v_vin}`} + + VIN: {metadata.vehicle?.v_vin ?? t("general.labels.na")} + ) : null} @@ -101,13 +104,11 @@ export default function WhiteBoardCard({ metadata }) { {t("general.labels.in")}: - {metadata.actual_in} + {metadata.actual_in} {t("general.labels.out")}: - - {metadata.scheduled_completion} - + {metadata.scheduled_completion} diff --git a/client/src/graphql/apollo-error-handling.js b/client/src/graphql/apollo-error-handling.js index 7ebd8754e..59d992097 100644 --- a/client/src/graphql/apollo-error-handling.js +++ b/client/src/graphql/apollo-error-handling.js @@ -4,6 +4,7 @@ import { auth } from "../firebase/firebase.utils"; //https://stackoverflow.com/questions/57163454/refreshing-a-token-with-apollo-client-firebase-auth const errorLink = onError( ({ graphQLErrors, networkError, operation, forward }) => { + console.log("In gql error") let access_token = window.localStorage.getItem("token"); if (graphQLErrors) { // User access token has expired diff --git a/client/src/pages/jobs-detail/jobs-detail.page.jsx b/client/src/pages/jobs-detail/jobs-detail.page.jsx index 831581343..7890ee45f 100644 --- a/client/src/pages/jobs-detail/jobs-detail.page.jsx +++ b/client/src/pages/jobs-detail/jobs-detail.page.jsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { useSubscription } from "@apollo/react-hooks"; +import { useQuery } from "@apollo/react-hooks"; import SpinComponent from "../../components/loading-spinner/loading-spinner.component"; import AlertComponent from "../../components/alert/alert.component"; import JobTombstone from "../../components/job-tombstone/job-tombstone.component"; @@ -11,7 +11,7 @@ import { useTranslation } from "react-i18next"; function JobsDetailPage({ match }) { const { jobId } = match.params; const { t } = useTranslation(); - const { loading, error, data } = useSubscription(GET_JOB_BY_PK, { + const { loading, error, data } = useQuery(GET_JOB_BY_PK, { variables: { id: jobId }, fetchPolicy: "network-only" }); @@ -22,7 +22,7 @@ function JobsDetailPage({ match }) { : t("titles.jobsdetail", { ro_number: data.jobs_by_pk.ro_number }); - }, [loading]); + }, [loading,data,t]); if (loading) return ; if (error) return ; diff --git a/client/src/pages/jobs/jobs.page.jsx b/client/src/pages/jobs/jobs.page.jsx index 615e34d38..73d29d521 100644 --- a/client/src/pages/jobs/jobs.page.jsx +++ b/client/src/pages/jobs/jobs.page.jsx @@ -14,7 +14,7 @@ export default function JobsPage() { useEffect(() => { document.title = t("titles.jobs"); - }, []); + }, [t]); if (error) return ; diff --git a/client/src/pages/manage/manage.page.jsx b/client/src/pages/manage/manage.page.jsx index 56ad79bc7..9d84f3c08 100644 --- a/client/src/pages/manage/manage.page.jsx +++ b/client/src/pages/manage/manage.page.jsx @@ -11,6 +11,7 @@ import ErrorBoundary from "../../components/error-boundary/error-boundary.compon const WhiteBoardPage = lazy(() => import("../white-board/white-board.page")); const JobsPage = lazy(() => import("../jobs/jobs.page")); const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page")); +const ProfilePage = lazy(() => import("../profile/profile.container.page")); const { Header, Content, Footer } = Layout; //This page will handle all routing for the entire application. @@ -19,7 +20,7 @@ export default function Manage({ match }) { useEffect(() => { document.title = t("titles.app"); - }, []); + }, [t]); return ( @@ -30,7 +31,8 @@ export default function Manage({ match }) { TODO: Suspended Loading in Manage Page...}> + fallback={
TODO: Suspended Loading in Manage Page...
} + > @@ -38,6 +40,12 @@ export default function Manage({ match }) { path={`${match.path}/jobs/:jobId`} component={JobsDetailPage} /> + +
diff --git a/client/src/pages/profile/profile.container.page.jsx b/client/src/pages/profile/profile.container.page.jsx new file mode 100644 index 000000000..4acf9c486 --- /dev/null +++ b/client/src/pages/profile/profile.container.page.jsx @@ -0,0 +1,6 @@ +import React from "react"; +import ProfilePage from "./profile.page"; + +export default function ProfileContainerPage() { + return ; +} diff --git a/client/src/pages/profile/profile.page.jsx b/client/src/pages/profile/profile.page.jsx new file mode 100644 index 000000000..0f8d1d243 --- /dev/null +++ b/client/src/pages/profile/profile.page.jsx @@ -0,0 +1,27 @@ +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Layout, Menu, Icon } from "antd"; +import ProfileSideBar from "../../components/profile-sidebar/profile-sidebar.component"; +import ProfileContent from "../../components/profile-content/profile-content.component"; + +export default function ProfilePage() { + const { t } = useTranslation(); + + useEffect(() => { + document.title = t("titles.profile"); + }, [t]); + + const [sidebarSelection, setSidebarSelection] = useState({ key: "profile" }); + return ( + + + + + + + + ); +} diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 0b627c30d..cf205f62a 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -10,13 +10,32 @@ "in": "In", "out": "Out", "na": "N/A", + "unknown": "Unknown", "save": "Save" } }, + "menus": { + "currentuser": { + "profile": "Profile", + "languageselector": "Language" + }, + "profilesidebar": { + "profile": "My Profile", + "shops": "My Shops" + } + }, + "titles": { "app": "Bodyshop by ImEX Systems", "jobs": "All Jobs | $t(titles.app)", - "jobsdetail": "Job {{ro_number}} | $t(titles.app)" + "jobsdetail": "Job {{ro_number}} | $t(titles.app)", + "profile": "My Profile | $t(titles.app)" + }, + + "profile": { + "errors": { + "state": "Error reading page state. Please refresh." + } }, "jobs": { diff --git a/server.pass.key b/server.pass.key deleted file mode 100644 index 516134f68..000000000 --- a/server.pass.key +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,1A1ACAB16FB7DBFA - -OUyvmLtCOnxYqlxwgp+ObGz1vI3BTxGgoI1S3+VkLOj9qzucmhD4MqDYDKRlEo0l -4NWTGoK3GDu+Fl5J/Ps4fvYrNOdmsS+1wCOLEt5GV18CYEdRx4C9zMIu1+XYsY0N -2Y3Jb66Mewe3vuL4LgVfiw4iI2f/k3mLGsurL88430DTdW8MpifGjFhdBOxMQ/xA -XrTtkxMquynL3LGELwzevAvv4ewFn8XMkKT4ZriuZwp58IMHg3xoAlMjxgtKzsy0 -zfgngqG6aOR6i8317erHTYSeCvF4BzP+s7Q9YPP8S2W2v4yiHGZ0XYiXgwrVQnRt -39PbU2g0l9iKzDsGnne3iLpQb4qrWM8ZJPZPcVgTbxAdZur6zNuifCJ+vrqlrtC7 -fJlmEJpwAH1H7ym85edkjF23fgEmIvyFIA1p7hbxtf+Dy723CHjrV7W3OZ2ecM+K -7HxvfU+XygfQyP+gIscV2ExgANAp+NdOnflkv3CrZQbEFMyAAjOYn63Cdrq/BMfc -gLs2V6xCQnWnt+WmQdoiXJgEniV2ECGAtNxYLfatjybjNDciqm2C9vPAME5ol6pt -l/wUnufnVWEYmlPzF0txtVZti5RZa8pUOnHBsHUuj1F7ftZtx85vbi26j7hW8ViT -nr52aMcUVvagyikF5WGVFr9pyHyx9tmECRbE243Akl3mLPllo2tvJC4wkLlJmDpn -4DdDk+A7JpfZ11KH+HWskVm26SdPODgq2v8BFzlsPm8C/z4jPooAI6FwLzpQ7Y1x -7S0Q5wEGO+XsAfB4BXpUHpZxPzr3FDqjjymd/gBcEJzC65/p2iJO+gAwLYCCIYB6 -fH3T5D6cUZ+84ijrF30wNHpkbGq877RValYm+DLI4Ui9oJrsQxl8QJw+BOhEfe+i -oeKGQESkMjwV+Ve/adHQENXtBGrZeTxvhyesTnT/R1k0VSLZrk030A5S+VhVMBiX -h3cvvD7gH0UFtL2viMqKXIw0GDSpk3K1/ygm5fLooONiPWz36dhoaZ6BYCksSemU -iyjIP4avCQ5N2zS4uWnIwHxdiM9/x4aMrXwZeAkufly8gFLXOrGbZEXaP3XtO/fP -+jeQUEx3O7/tjnrbhtVRzgC92B2G67Ay5MWj7nRuLaCXtDuKvbH3mkqDEiXmfpuL -ck9d36GsWUjRz7mf9sc/lIXJmLL/kX+ticogH9GsAKaFat9fshIWsyIhi9UYxMt0 -wa471bxWGnZgnAXTgrVUjjPjzsU9arY9pIeRR5xM0hSBAbPyemRhkIl+jZ/xKArd -TnFXePQPuF/1Grh8LzjpipgDCs5/zwguWBzB+KmOKycVoTYfG66CWtVFTyhZDWZW -cBiBzUT5VIvPBQs6b0Mbx5R2WyIsIAZzWUHLiO7lFctUbRxTgVhgZuNJjaOI5IYi -1wCfACUOE2uBjkzxI+DHX0lywMmuy8vK4jsXVBxUdjbJouIfh/NBvVUFR0/st7G7 -1qjo0ys1WBTfKFX0nGs+6zSxrMJMVxusc6Vt/sqKeLRZH3PkAZjUf8ABc0aIO6Lz -I3714iwiObVqS2xeZamzB/9IlyEmlkrFXF4E2YhWmKxN168merd1KybylTxqMs+l ------END RSA PRIVATE KEY-----