{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
-

- ) : null}
-
-
{bodyshop.shopname || ""}
-
{`${bodyshop.address1 || ""}`}
-
{`${bodyshop.address2 || ""}`}
-
{`${bodyshop.city || ""} ${bodyshop.state || ""} ${
- bodyshop.zip_post || ""
- }`}
+

) : null}
+
+
+ {bodyshop.shopname || ""}
+
+
+ {`${bodyshop.address1 || ""}${bodyshop.address2 ? ", " : ""}${
+ bodyshop.address2 || ""
+ }`.trim()}
+
+
+ {`${bodyshop.city || ""}${
+ bodyshop.city && bodyshop.state ? ", " : ""
+ }${bodyshop.state || ""} ${bodyshop.zip_post || ""}`.trim()}
+
{t("csi.labels.title")}
-
{`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`}
+
{t("csi.labels.greeting", {
+ name: job.ownr_co_nm || job.ownr_fn || "",
+ })}
- {`At ${
+ {t("csi.labels.intro", {
+ shopname:
bodyshop.shopname || ""
- }, we value your feedback. We would love to
- hear what you have to say. Please fill out the form below.`}
+ })}
@@ -158,21 +213,42 @@ export function CsiContainerPage({currentUser}) {
}}
>
- )}
+ )}
- {`Copyright ImEX.Online. Survey ID: ${surveyId}`}
+ {t("csi.labels.copyright")}{" "}
+ {t("csi.fields.surveyid", {surveyId: surveyId})}
);
+ }
}
diff --git a/client/src/pages/parts-queue/parts-queue.page.container.jsx b/client/src/pages/parts-queue/parts-queue.page.container.jsx
index fb0f352a7..6c69d9609 100644
--- a/client/src/pages/parts-queue/parts-queue.page.container.jsx
+++ b/client/src/pages/parts-queue/parts-queue.page.container.jsx
@@ -1,9 +1,10 @@
import React, {useEffect} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
+import PartsQueueDetailCard from "../../components/parts-queue-card/parts-queue-card.component";
+import PartsQueueList from "../../components/parts-queue-list/parts-queue.list.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
-import PartsQueuePage from "./parts-queue.page.component";
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
@@ -23,7 +24,8 @@ export function PartsQueuePageContainer({setBreadcrumbs, setSelectedHeader}) {
return (
-
+
+
);
}
diff --git a/client/src/pages/shop-csi/shop-csi.container.page.jsx b/client/src/pages/shop-csi/shop-csi.container.page.jsx
index 25a61f7b1..be3cc2d64 100644
--- a/client/src/pages/shop-csi/shop-csi.container.page.jsx
+++ b/client/src/pages/shop-csi/shop-csi.container.page.jsx
@@ -1,20 +1,17 @@
-import {Col, Row} from "antd";
import {useQuery} from "@apollo/client";
+import {Col, Row} from "antd";
import React, {useEffect} from "react";
import {useTranslation} from "react-i18next";
import {connect} from "react-redux";
-import {useLocation} from "react-router-dom";
import {createStructuredSelector} from "reselect";
import AlertComponent from "../../components/alert/alert.component";
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
import CsiResponseListPaginated
- from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
-import {QUERY_CSI_RESPONSE_PAGINATED} from "../../graphql/csi.queries";
-import {setBreadcrumbs, setSelectedHeader} from "../../redux/application/application.actions";
-import {selectBodyshop} from "../../redux/user/user.selectors";
+ from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
-import {pageLimit} from "../../utils/config";
-import queryString from "query-string";
+import {QUERY_CSI_RESPONSE_PAGINATED} from "../../graphql/csi.queries";
+import {setBreadcrumbs, setSelectedHeader,} from "../../redux/application/application.actions";
+import {selectBodyshop} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -25,29 +22,18 @@ const mapDispatchToProps = (dispatch) => ({
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
-export function ShopCsiContainer({bodyshop, setBreadcrumbs, setSelectedHeader}) {
+export function ShopCsiContainer({
+ bodyshop,
+ setBreadcrumbs,
+ setSelectedHeader,
+ }) {
const {t} = useTranslation();
- const searchParams = queryString.parse(useLocation().search);
- const {page, sortcolumn, sortorder} = searchParams;
const {loading, error, data, refetch} = useQuery(
QUERY_CSI_RESPONSE_PAGINATED,
{
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
- variables: {
- offset: page ? (page - 1) * pageLimit : 0,
- limit: pageLimit,
- order: [
- {
- [sortcolumn || "completedon"]: sortorder
- ? sortorder === "descend"
- ? "desc_nulls_last"
- : "asc"
- : "desc_nulls_last",
- },
- ],
- },
}
);
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 5b9032ecd..23a6015aa 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -838,17 +838,24 @@
"creating": "Error creating survey {{message}}",
"notconfigured": "You do not have any current CSI Question Sets configured.",
"notfoundsubtitle": "We were unable to find a survey using the link you provided. Please ensure the URL is correct or reach out to your shop for more help.",
- "notfoundtitle": "No survey found."
+ "notfoundtitle": "No survey found.",
+ "surveycompletetitle": "Survey previously completed",
+ "surveycompletesubtitle": "This survey was already completed on {{date}}."
},
"fields": {
"completedon": "Completed On",
- "created_at": "Created At"
+ "created_at": "Created At",
+ "surveyid": "Survey ID {{surveyId}}",
+ "validuntil": "Valid Until"
},
"labels": {
- "nologgedinuser": "Please log out of ImEX Online",
- "nologgedinuser_sub": "Users of ImEX Online cannot complete CSI surveys while logged in. Please log out and try again.",
+ "nologgedinuser": "Please log out of $t(titles.app)",
+ "nologgedinuser_sub": "Users of $t(titles.app) cannot complete CSI surveys while logged in. Please log out and try again.",
"noneselected": "No response selected.",
- "title": "Customer Satisfaction Survey"
+ "title": "Customer Satisfaction Survey",
+ "greeting": "Hi {{name}}!",
+ "intro": "At {{shopname}}, we value your feedback. We would love to hear what you have to say. Please fill out the form below.",
+ "copyright": "Copyright © $t(titles.app). All Rights Reserved."
},
"successes": {
"created": "CSI created successfully. ",
@@ -1857,6 +1864,7 @@
"override_header": "Override estimate header on import?",
"ownerassociation": "Owner Association",
"parts": "Parts",
+ "parts_lines": "Parts Lines",
"parts_received": "Parts Rec.",
"parts_tax_rates": "Parts Tax rates",
"partsfilter": "Parts Only",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index db92c9d66..299364c1b 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -838,17 +838,24 @@
"creating": "",
"notconfigured": "",
"notfoundsubtitle": "",
- "notfoundtitle": ""
+ "notfoundtitle": "",
+ "surveycompletetitle": "",
+ "surveycompletesubtitle": ""
},
"fields": {
"completedon": "",
- "created_at": ""
+ "created_at": "",
+ "surveyid": "",
+ "validuntil": ""
},
"labels": {
"nologgedinuser": "",
"nologgedinuser_sub": "",
"noneselected": "",
- "title": ""
+ "title": "",
+ "greeting": "",
+ "intro": "",
+ "copyright": ""
},
"successes": {
"created": "",
@@ -1857,6 +1864,7 @@
"override_header": "¿Anular encabezado estimado al importar?",
"ownerassociation": "",
"parts": "Partes",
+ "parts_lines": "",
"parts_received": "",
"parts_tax_rates": "",
"partsfilter": "",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 5ae7ba73d..a6c7c9912 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -838,17 +838,24 @@
"creating": "",
"notconfigured": "",
"notfoundsubtitle": "",
- "notfoundtitle": ""
+ "notfoundtitle": "",
+ "surveycompletetitle": "",
+ "surveycompletesubtitle": ""
},
"fields": {
"completedon": "",
- "created_at": ""
+ "created_at": "",
+ "surveyid": "",
+ "validuntil": ""
},
"labels": {
"nologgedinuser": "",
"nologgedinuser_sub": "",
"noneselected": "",
- "title": ""
+ "title": "",
+ "greeting": "",
+ "intro": "",
+ "copyright": ""
},
"successes": {
"created": "",
@@ -1857,6 +1864,7 @@
"override_header": "Remplacer l'en-tête d'estimation à l'importation?",
"ownerassociation": "",
"parts": "les pièces",
+ "parts_lines": "",
"parts_received": "",
"parts_tax_rates": "",
"partsfilter": "",
diff --git a/server.js b/server.js
index 1eaa4b8ec..dbb0d0a5e 100644
--- a/server.js
+++ b/server.js
@@ -74,6 +74,7 @@ app.use('/adm', require("./server/routes/adminRoutes"));
app.use('/tech', require("./server/routes/techRoutes"));
app.use('/intellipay', require("./server/routes/intellipayRoutes"));
app.use('/cdk', require("./server/routes/cdkRoutes"));
+app.use('/csi', require("./server/routes/csiRoutes"));
// Default route for forbidden access
app.get("/", (req, res) => {
diff --git a/server/csi/csi.js b/server/csi/csi.js
new file mode 100644
index 000000000..819a9ebc7
--- /dev/null
+++ b/server/csi/csi.js
@@ -0,0 +1,2 @@
+exports.lookup = require("./lookup").default;
+exports.submit = require("./submit").default;
\ No newline at end of file
diff --git a/server/csi/lookup.js b/server/csi/lookup.js
new file mode 100644
index 000000000..a3c156e96
--- /dev/null
+++ b/server/csi/lookup.js
@@ -0,0 +1,24 @@
+const path = require("path");
+const queries = require("../graphql-client/queries");
+const logger = require("../utils/logger");
+require("dotenv").config({
+ path: path.resolve(
+ process.cwd(),
+ `.env.${process.env.NODE_ENV || "development"}`
+ ),
+});
+
+const client = require("../graphql-client/graphql-client").client;
+
+exports.default = async (req, res) => {
+ try {
+ logger.log("csi-surveyID-lookup", "DEBUG", "csi", req.body.surveyId, null);
+ const gql_response = await client.request(queries.QUERY_SURVEY, {
+ surveyId: req.body.surveyId,
+ });
+ res.status(200).json(gql_response);
+ } catch (error) {
+ logger.log("csi-surveyID-lookup", "ERROR", "csi", req.body.surveyId, error);
+ res.status(400).json(error);
+ }
+};
diff --git a/server/csi/submit.js b/server/csi/submit.js
new file mode 100644
index 000000000..ea6e54164
--- /dev/null
+++ b/server/csi/submit.js
@@ -0,0 +1,29 @@
+const path = require("path");
+const queries = require("../graphql-client/queries");
+const logger = require("../utils/logger");
+require("dotenv").config({
+ path: path.resolve(
+ process.cwd(),
+ `.env.${process.env.NODE_ENV || "development"}`
+ ),
+});
+
+const client = require("../graphql-client/graphql-client").client;
+
+exports.default = async (req, res) => {
+ try {
+ logger.log("csi-surveyID-submit", "DEBUG", "csi", req.body.surveyId, null);
+ const gql_response = await client.request(queries.COMPLETE_SURVEY, {
+ surveyId: req.body.surveyId,
+ survey: {
+ response: req.body.values,
+ valid: false,
+ completedon: new Date(),
+ },
+ });
+ res.status(200).json(gql_response);
+ } catch (error) {
+ logger.log("csi-surveyID-submit", "ERROR", "csi", req.body.surveyId, error);
+ res.status(400).json(error);
+ }
+};
diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js
index 59bbaf9c1..369b394b4 100644
--- a/server/graphql-client/queries.js
+++ b/server/graphql-client/queries.js
@@ -2156,3 +2156,23 @@ exports.ACTIVE_SHOP_BY_USER = `query ACTIVE_SHOP_BY_USER($user: String) {
shopid
}
}`;
+
+exports.QUERY_SURVEY = `query QUERY_SURVEY($surveyId: uuid!) {
+ csi_by_pk(id: $surveyId) {
+ completedon
+ csiquestion {
+ id
+ config
+ }
+ id
+ relateddata
+ valid
+ validuntil
+ }
+}`;
+
+exports.COMPLETE_SURVEY = `mutation COMPLETE_SURVEY($surveyId: uuid!, $survey: csi_set_input) {
+ update_csi(where: { id: { _eq: $surveyId } }, _set: $survey) {
+ affected_rows
+ }
+ }`;
\ No newline at end of file
diff --git a/server/routes/csiRoutes.js b/server/routes/csiRoutes.js
new file mode 100644
index 000000000..11993ff8f
--- /dev/null
+++ b/server/routes/csiRoutes.js
@@ -0,0 +1,8 @@
+const express = require("express");
+const router = express.Router();
+const {lookup, submit} = require("../csi/csi");
+
+router.post("/lookup", lookup);
+router.post("/submit", submit);
+
+module.exports = router;