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 = (
+
+ );
+
+ 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 (
-
+
+
+ {!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-----