Merged in feature/IO-2626-CSI-Pages (pull request #1253)
IO-2626 CSI Pages
This commit is contained in:
@@ -5,6 +5,7 @@ import React, { useEffect } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { QUERY_CSI_RESPONSE_BY_PK } from "../../graphql/csi.queries";
|
import { QUERY_CSI_RESPONSE_BY_PK } from "../../graphql/csi.queries";
|
||||||
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
@@ -44,6 +45,13 @@ export default function CsiResponseFormContainer() {
|
|||||||
readOnly
|
readOnly
|
||||||
componentList={data.csi_by_pk.csiquestion.config}
|
componentList={data.csi_by_pk.csiquestion.config}
|
||||||
/>
|
/>
|
||||||
|
{data.csi_by_pk.validuntil ? (
|
||||||
|
<>
|
||||||
|
{t("csi.fields.validuntil")}
|
||||||
|
{": "}
|
||||||
|
<DateFormatter>{data.csi_by_pk.validuntil}</DateFormatter>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import React, { useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { pageLimit } from "../../utils/config";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import {pageLimit} from "../../utils/config";
|
import OwnerNameDisplay, {
|
||||||
|
OwnerNameDisplayFunction,
|
||||||
|
} from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
export default function CsiResponseListPaginated({
|
export default function CsiResponseListPaginated({
|
||||||
refetch,
|
refetch,
|
||||||
@@ -16,23 +18,23 @@ export default function CsiResponseListPaginated({
|
|||||||
total,
|
total,
|
||||||
}) {
|
}) {
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const { responseid, page, sortcolumn, sortorder } = search;
|
const { responseid } = search;
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: { text: "" },
|
filteredInfo: { text: "" },
|
||||||
|
page: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.ro_number"),
|
title: t("jobs.fields.ro_number"),
|
||||||
dataIndex: "ro_number",
|
dataIndex: "ro_number",
|
||||||
key: "ro_number",
|
key: "ro_number",
|
||||||
width: "8%",
|
|
||||||
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
|
||||||
sortOrder: sortcolumn === "ro_number" && sortorder,
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Link to={"/manage/jobs/" + record.job.id}>
|
<Link to={"/manage/jobs/" + record.job.id}>
|
||||||
{record.job.ro_number || t("general.labels.na")}
|
{record.job.ro_number || t("general.labels.na")}
|
||||||
@@ -41,15 +43,18 @@ export default function CsiResponseListPaginated({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.owner"),
|
title: t("jobs.fields.owner"),
|
||||||
dataIndex: "owner",
|
dataIndex: "owner_name",
|
||||||
key: "owner",
|
key: "owner_name",
|
||||||
ellipsis: true,
|
sorter: (a, b) =>
|
||||||
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
|
alphaSort(
|
||||||
width: "25%",
|
OwnerNameDisplayFunction(a.job),
|
||||||
sortOrder: sortcolumn === "owner" && sortorder,
|
OwnerNameDisplayFunction(b.job)
|
||||||
|
),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "owner_name" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.job.owner ? (
|
return record.job.ownerid ? (
|
||||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
<Link to={"/manage/owners/" + record.job.ownerid}>
|
||||||
<OwnerNameDisplay ownerObject={record.job} />
|
<OwnerNameDisplay ownerObject={record.job} />
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
@@ -64,9 +69,9 @@ export default function CsiResponseListPaginated({
|
|||||||
dataIndex: "completedon",
|
dataIndex: "completedon",
|
||||||
key: "completedon",
|
key: "completedon",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
sorter: (a, b) => a.completedon - b.completedon,
|
sorter: (a, b) => dateSort(a.completedon, b.completedon),
|
||||||
width: "25%",
|
sortOrder:
|
||||||
sortOrder: sortcolumn === "completedon" && sortorder,
|
state.sortedInfo.columnKey === "completedon" && state.sortedInfo.order,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.completedon ? (
|
return record.completedon ? (
|
||||||
<DateFormatter>{record.completedon}</DateFormatter>
|
<DateFormatter>{record.completedon}</DateFormatter>
|
||||||
@@ -76,11 +81,12 @@ export default function CsiResponseListPaginated({
|
|||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
setState({
|
||||||
search.page = pagination.current;
|
...state,
|
||||||
search.sortcolumn = sorter.columnKey;
|
filteredInfo: filters,
|
||||||
search.sortorder = sorter.order;
|
sortedInfo: sorter,
|
||||||
history.push({ search: queryString.stringify(search) });
|
page: pagination.current,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnRowClick = (record) => {
|
const handleOnRowClick = (record) => {
|
||||||
@@ -108,7 +114,7 @@ export default function CsiResponseListPaginated({
|
|||||||
pagination={{
|
pagination={{
|
||||||
position: "top",
|
position: "top",
|
||||||
pageSize: pageLimit,
|
pageSize: pageLimit,
|
||||||
current: parseInt(page || 1),
|
current: parseInt(state.page || 1),
|
||||||
total: total,
|
total: total,
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -122,13 +128,6 @@ export default function CsiResponseListPaginated({
|
|||||||
selectedRowKeys: [responseid],
|
selectedRowKeys: [responseid],
|
||||||
type: "radio",
|
type: "radio",
|
||||||
}}
|
}}
|
||||||
onRow={(record, rowIndex) => {
|
|
||||||
return {
|
|
||||||
onClick: (event) => {
|
|
||||||
handleOnRowClick(record);
|
|
||||||
}, // click row
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
|
||||||
import { Form } from "antd";
|
import { Form } from "antd";
|
||||||
|
import React from "react";
|
||||||
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../config-form-components/config-form-components.component";
|
||||||
|
|
||||||
export default function ShopCsiConfigForm({ selectedCsi }) {
|
export default function ShopCsiConfigForm({ selectedCsi }) {
|
||||||
@@ -9,7 +9,7 @@ export default function ShopCsiConfigForm({ selectedCsi }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
The Config Form {readOnly}
|
{readOnly}
|
||||||
{selectedCsi && (
|
{selectedCsi && (
|
||||||
<Form form={form} onFinish={handleFinish}>
|
<Form form={form} onFinish={handleFinish}>
|
||||||
<ConfigFormComponents
|
<ConfigFormComponents
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CheckCircleFilled } from "@ant-design/icons";
|
import { CheckCircleFilled } from "@ant-design/icons";
|
||||||
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Col, List, Row } from "antd";
|
import { Button, Col, List, Row } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useQuery } from "@apollo/client";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { GET_ALL_QUESTION_SETS } from "../../graphql/csi.queries";
|
import { GET_ALL_QUESTION_SETS } from "../../graphql/csi.queries";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
@@ -21,7 +21,6 @@ export default function ShopCsiConfig() {
|
|||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
The Config Form
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={3}>
|
<Col span={3}>
|
||||||
<List
|
<List
|
||||||
@@ -42,7 +41,8 @@ export default function ShopCsiConfig() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={21}>
|
<Col span={1} />
|
||||||
|
<Col span={20}>
|
||||||
<ShopCsiConfigForm selectedCsi={selectedCsi} />
|
<ShopCsiConfigForm selectedCsi={selectedCsi} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@@ -57,19 +57,15 @@ export const INSERT_CSI = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
||||||
query QUERY_CSI_RESPONSE_PAGINATED(
|
query QUERY_CSI_RESPONSE_PAGINATED {
|
||||||
$offset: Int
|
csi(order_by: { completedon: desc_nulls_last }) {
|
||||||
$limit: Int
|
|
||||||
$order: [csi_order_by!]!
|
|
||||||
) {
|
|
||||||
csi(offset: $offset, limit: $limit, order_by: $order) {
|
|
||||||
id
|
id
|
||||||
completedon
|
completedon
|
||||||
job {
|
job {
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
|
ownerid
|
||||||
ro_number
|
ro_number
|
||||||
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,6 +79,7 @@ export const QUERY_CSI_RESPONSE_PAGINATED = gql`
|
|||||||
export const QUERY_CSI_RESPONSE_BY_PK = gql`
|
export const QUERY_CSI_RESPONSE_BY_PK = gql`
|
||||||
query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) {
|
query QUERY_CSI_RESPONSE_BY_PK($id: uuid!) {
|
||||||
csi_by_pk(id: $id) {
|
csi_by_pk(id: $id) {
|
||||||
|
completedon
|
||||||
relateddata
|
relateddata
|
||||||
valid
|
valid
|
||||||
validuntil
|
validuntil
|
||||||
|
|||||||
@@ -1,88 +1,62 @@
|
|||||||
import { useQuery, useMutation } from "@apollo/client";
|
// import { useMutation, useQuery } from "@apollo/client";
|
||||||
import { Form, Layout, Typography, Button, Result } from "antd";
|
import { Button, Form, Layout, Result, Typography } from "antd";
|
||||||
import React, { useState } from "react";
|
import axios from "axios";
|
||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
|
import ConfigFormComponents from "../../components/config-form-components/config-form-components.component";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import { QUERY_SURVEY, COMPLETE_SURVEY } from "../../graphql/csi.queries";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
|
import { DateTimeFormat } from "./../../utils/DateFormatter";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
|
||||||
});
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
|
export default connect(mapStateToProps, mapDispatchToProps)(CsiContainerPage);
|
||||||
|
|
||||||
export function CsiContainerPage({ currentUser }) {
|
export function CsiContainerPage({ currentUser }) {
|
||||||
const { surveyId } = useParams();
|
const { surveyId } = useParams();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const [axiosResponse, setAxiosResponse] = useState(null);
|
||||||
const [submitting, setSubmitting] = useState({
|
const [submitting, setSubmitting] = useState({
|
||||||
loading: false,
|
loading: false,
|
||||||
submitted: false,
|
submitted: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(QUERY_SURVEY, {
|
|
||||||
variables: { surveyId },
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
nextFetchPolicy: "network-only",
|
|
||||||
});
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [completeSurvey] = useMutation(COMPLETE_SURVEY);
|
|
||||||
if (loading) return <LoadingSpinner />;
|
|
||||||
|
|
||||||
if (error || !!!data.csi_by_pk)
|
const getAxiosData = useCallback(async () => {
|
||||||
return (
|
try {
|
||||||
<div>
|
setSubmitting((prevSubmitting) => ({ ...prevSubmitting, loading: true }));
|
||||||
<Result
|
const response = await axios.post("/csi/lookup", { surveyId });
|
||||||
status="error"
|
setSubmitting((prevSubmitting) => ({
|
||||||
title={t("csi.errors.notfoundtitle")}
|
...prevSubmitting,
|
||||||
subTitle={t("csi.errors.notfoundsubtitle")}
|
|
||||||
>
|
|
||||||
{error ? (
|
|
||||||
<div>ERROR: {error.graphQLErrors.map((e) => e.message)}</div>
|
|
||||||
) : null}
|
|
||||||
</Result>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
|
||||||
setSubmitting({ ...submitting, loading: true });
|
|
||||||
|
|
||||||
const result = await completeSurvey({
|
|
||||||
variables: {
|
|
||||||
surveyId,
|
|
||||||
survey: {
|
|
||||||
response: values,
|
|
||||||
valid: false,
|
|
||||||
completedon: new Date(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!!!result.errors) {
|
|
||||||
setSubmitting({ ...submitting, loading: false, submitted: true });
|
|
||||||
} else {
|
|
||||||
setSubmitting({
|
|
||||||
...submitting,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
error: JSON.stringify(result.errors),
|
}));
|
||||||
|
setAxiosResponse(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Something went wrong...: ${error.message}`);
|
||||||
|
console.dir({
|
||||||
|
stack: error?.stack,
|
||||||
|
message: error?.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}, [setAxiosResponse, surveyId]);
|
||||||
|
|
||||||
const {
|
useEffect(() => {
|
||||||
relateddata: { bodyshop, job },
|
getAxiosData().catch((err) =>
|
||||||
csiquestion: { config: csiquestions },
|
console.error(
|
||||||
} = data.csi_by_pk;
|
`Something went wrong fetching axios data: ${err.message || ""}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [getAxiosData]);
|
||||||
|
|
||||||
if (currentUser && currentUser.authorized)
|
// Return if authorized
|
||||||
|
if (currentUser && currentUser.authorized) {
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
||||||
@@ -94,85 +68,171 @@ export function CsiContainerPage({ currentUser }) {
|
|||||||
/>
|
/>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
if (submitting.loading) return <LoadingSpinner />;
|
||||||
<Layout
|
|
||||||
style={{ height: "100vh", display: "flex", flexDirection: "column" }}
|
const handleFinish = async (values) => {
|
||||||
>
|
try {
|
||||||
<div
|
setSubmitting({ ...submitting, loading: true, submitting: true });
|
||||||
style={{
|
const result = await axios.post("/csi/submit", { surveyId, values });
|
||||||
display: "flex",
|
console.log("result", result);
|
||||||
flexDirection: "column",
|
if (!!!result.errors && result.data.update_csi.affected_rows > 0) {
|
||||||
alignItems: "center",
|
setSubmitting({ ...submitting, loading: false, submitted: true });
|
||||||
}}
|
}
|
||||||
>
|
} catch (error) {
|
||||||
<div style={{ display: "flex", alignItems: "center", margin: "2em" }}>
|
console.error(`Something went wrong...: ${error.message}`);
|
||||||
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
console.dir({
|
||||||
<img src={bodyshop.logo_img_path.src} alt="Logo" />
|
stack: error?.stack,
|
||||||
) : null}
|
message: error?.message,
|
||||||
<div style={{ margin: "2em" }}>
|
});
|
||||||
<strong>{bodyshop.shopname || ""}</strong>
|
}
|
||||||
<div>{`${bodyshop.address1 || ""}`}</div>
|
};
|
||||||
<div>{`${bodyshop.address2 || ""}`}</div>
|
|
||||||
<div>{`${bodyshop.city || ""} ${bodyshop.state || ""} ${
|
if (!axiosResponse || axiosResponse.csi_by_pk === null) {
|
||||||
bodyshop.zip_post || ""
|
// Do something here , this is where you would return a loading box or something
|
||||||
}`}</div>
|
return (
|
||||||
|
<>
|
||||||
|
<Layout style={{ display: "flex", flexDirection: "column" }}>
|
||||||
|
<Layout.Content
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
margin: "2em 4em",
|
||||||
|
padding: "2em",
|
||||||
|
overflowY: "auto",
|
||||||
|
textAlign: "center",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<Result
|
||||||
|
status="error"
|
||||||
|
title={t("csi.errors.notfoundtitle")}
|
||||||
|
subTitle={t("csi.errors.notfoundsubtitle")}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Layout.Content>
|
||||||
|
<Layout.Footer>
|
||||||
|
{t("csi.labels.copyright")}{" "}
|
||||||
|
{t("csi.fields.surveyid", { surveyId: surveyId })}
|
||||||
|
</Layout.Footer>
|
||||||
|
</Layout>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const {
|
||||||
|
relateddata: { bodyshop, job },
|
||||||
|
csiquestion: { config: csiquestions },
|
||||||
|
} = axiosResponse.csi_by_pk;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{ display: "flex", flexDirection: "column" }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", margin: "2em" }}>
|
||||||
|
{bodyshop.logo_img_path && bodyshop.logo_img_path.src ? (
|
||||||
|
<img src={bodyshop.logo_img_path.src} alt="Logo" />
|
||||||
|
) : null}
|
||||||
|
<div style={{ margin: "2em", verticalAlign: "middle" }}>
|
||||||
|
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||||
|
{bodyshop.shopname || ""}
|
||||||
|
</Typography.Title>
|
||||||
|
<Typography.Paragraph style={{ margin: 0 }}>
|
||||||
|
{`${bodyshop.address1 || ""}${bodyshop.address2 ? ", " : ""}${
|
||||||
|
bodyshop.address2 || ""
|
||||||
|
}`.trim()}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
<Typography.Paragraph style={{ margin: 0 }}>
|
||||||
|
{`${bodyshop.city || ""}${
|
||||||
|
bodyshop.city && bodyshop.state ? ", " : ""
|
||||||
|
}${bodyshop.state || ""} ${bodyshop.zip_post || ""}`.trim()}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
||||||
|
<strong>
|
||||||
|
{t("csi.labels.greeting", {
|
||||||
|
name: job.ownr_co_nm || job.ownr_fn || "",
|
||||||
|
})}
|
||||||
|
</strong>
|
||||||
|
<Typography.Paragraph>
|
||||||
|
{t("csi.labels.intro", { shopname: bodyshop.shopname || "" })}
|
||||||
|
</Typography.Paragraph>
|
||||||
</div>
|
</div>
|
||||||
<Typography.Title>{t("csi.labels.title")}</Typography.Title>
|
|
||||||
<strong>{`Hi ${job.ownr_co_nm || job.ownr_fn || ""}!`}</strong>
|
|
||||||
<Typography.Paragraph>
|
|
||||||
{`At ${
|
|
||||||
bodyshop.shopname || ""
|
|
||||||
}, we value your feedback. We would love to
|
|
||||||
hear what you have to say. Please fill out the form below.`}
|
|
||||||
</Typography.Paragraph>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{submitting.error ? (
|
{submitting.error ? (
|
||||||
<AlertComponent message={submitting.error} type="error" />
|
<AlertComponent message={submitting.error} type="error" />
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{submitting.submitted ? (
|
{submitting.submitted ? (
|
||||||
<Layout.Content
|
<Layout.Content
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
margin: "2em 4em",
|
margin: "2em 4em",
|
||||||
padding: "2em",
|
padding: "2em",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Result
|
<Result
|
||||||
status="success"
|
status="success"
|
||||||
title={t("csi.successes.submitted")}
|
title={t("csi.successes.submitted")}
|
||||||
subTitle={t("csi.successes.submittedsub")}
|
subTitle={t("csi.successes.submittedsub")}
|
||||||
/>
|
/>
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
) : (
|
) : (
|
||||||
<Layout.Content
|
<Layout.Content
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
margin: "2em 4em",
|
margin: "2em 4em",
|
||||||
padding: "2em",
|
padding: "2em",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form form={form} onFinish={handleFinish}>
|
<Form form={form} onFinish={handleFinish}>
|
||||||
<ConfigFormComponents componentList={csiquestions} />
|
{axiosResponse.csi_by_pk.valid ? (
|
||||||
<Button
|
<>
|
||||||
loading={submitting.loading}
|
<ConfigFormComponents componentList={csiquestions} />
|
||||||
type="primary"
|
<Button
|
||||||
htmlType="submit"
|
loading={submitting.loading}
|
||||||
>
|
type="primary"
|
||||||
{t("general.actions.submit")}
|
htmlType="submit"
|
||||||
</Button>
|
style={{ float: "right" }}
|
||||||
</Form>
|
>
|
||||||
</Layout.Content>
|
{t("general.actions.submit")}
|
||||||
)}
|
</Button>
|
||||||
|
</>
|
||||||
<Layout.Footer>
|
) : (
|
||||||
{`Copyright ImEX.Online. Survey ID: ${surveyId}`}
|
<>
|
||||||
</Layout.Footer>
|
<Result
|
||||||
</Layout>
|
title={t("csi.errors.surveycompletetitle")}
|
||||||
);
|
status="warning"
|
||||||
|
subTitle={t("csi.errors.surveycompletesubtitle", {
|
||||||
|
date: DateTimeFormat(axiosResponse.csi_by_pk.completedon),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Typography.Paragraph
|
||||||
|
type="secondary"
|
||||||
|
style={{ textAlign: "center" }}
|
||||||
|
>
|
||||||
|
{t("csi.successes.submittedsub")}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
</Layout.Content>
|
||||||
|
)}
|
||||||
|
<Layout.Footer>
|
||||||
|
{t("csi.labels.copyright")}{" "}
|
||||||
|
{t("csi.fields.surveyid", { surveyId: surveyId })}
|
||||||
|
</Layout.Footer>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import { Row, Col } from "antd";
|
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import queryString from "query-string";
|
import { Col, Row } from "antd";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import AlertComponent from "../../components/alert/alert.component";
|
import AlertComponent from "../../components/alert/alert.component";
|
||||||
import CsiResponseFormContainer from "../../components/csi-response-form/csi-response-form.container";
|
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 CsiResponseListPaginated from "../../components/csi-response-list-paginated/csi-response-list-paginated.component";
|
||||||
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries";
|
import { QUERY_CSI_RESPONSE_PAGINATED } from "../../graphql/csi.queries";
|
||||||
import {
|
import {
|
||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
|
||||||
import {pageLimit} from "../../utils/config";
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
@@ -33,28 +31,11 @@ export function ShopCsiContainer({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const searchParams = queryString.parse(useLocation().search);
|
|
||||||
const { page, sortcolumn, sortorder } = searchParams;
|
|
||||||
|
|
||||||
const { loading, error, data, refetch } = useQuery(
|
const { loading, error, data, refetch } = useQuery(
|
||||||
QUERY_CSI_RESPONSE_PAGINATED,
|
QUERY_CSI_RESPONSE_PAGINATED,
|
||||||
{
|
{
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
variables: {
|
|
||||||
//search: search || "",
|
|
||||||
offset: page ? (page - 1) * pageLimit : 0,
|
|
||||||
limit: pageLimit,
|
|
||||||
order: [
|
|
||||||
{
|
|
||||||
[sortcolumn || "completedon"]: sortorder
|
|
||||||
? sortorder === "descend"
|
|
||||||
? "desc_nulls_last"
|
|
||||||
: "asc"
|
|
||||||
: "desc_nulls_last",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -73,12 +54,7 @@ export function ShopCsiContainer({
|
|||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RbacWrapper
|
<RbacWrapper action="csi:page">
|
||||||
action="csi:page"
|
|
||||||
// noauth={
|
|
||||||
// <AlertComponent message="You don't have acess to see this screen." />
|
|
||||||
// }
|
|
||||||
>
|
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col span={10}>
|
<Col span={10}>
|
||||||
<CsiResponseListPaginated
|
<CsiResponseListPaginated
|
||||||
|
|||||||
@@ -838,20 +838,27 @@
|
|||||||
"creating": "Error creating survey {{message}}",
|
"creating": "Error creating survey {{message}}",
|
||||||
"notconfigured": "You do not have any current CSI Question Sets configured.",
|
"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.",
|
"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": {
|
"fields": {
|
||||||
"completedon": "Completed On",
|
"completedon": "Completed On",
|
||||||
"created_at": "Created At"
|
"created_at": "Created At",
|
||||||
|
"surveyid": "Survey ID {{surveyId}}",
|
||||||
|
"validuntil": "Valid Until"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"nologgedinuser": "Please log out of ImEX Online",
|
"nologgedinuser": "Please log out of $t(titles.app)",
|
||||||
"nologgedinuser_sub": "Users of ImEX Online cannot complete CSI surveys while logged in. Please log out and try again.",
|
"nologgedinuser_sub": "Users of $t(titles.app) cannot complete CSI surveys while logged in. Please log out and try again.",
|
||||||
"noneselected": "No response selected.",
|
"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": {
|
"successes": {
|
||||||
"created": "CSI created successfully. ",
|
"created": "CSI created successfully.",
|
||||||
"submitted": "Your responses have been submitted successfully.",
|
"submitted": "Your responses have been submitted successfully.",
|
||||||
"submittedsub": "Your input is highly appreciated."
|
"submittedsub": "Your input is highly appreciated."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -838,17 +838,24 @@
|
|||||||
"creating": "",
|
"creating": "",
|
||||||
"notconfigured": "",
|
"notconfigured": "",
|
||||||
"notfoundsubtitle": "",
|
"notfoundsubtitle": "",
|
||||||
"notfoundtitle": ""
|
"notfoundtitle": "",
|
||||||
|
"surveycompletetitle": "",
|
||||||
|
"surveycompletesubtitle": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "",
|
"completedon": "",
|
||||||
"created_at": ""
|
"created_at": "",
|
||||||
|
"surveyid": "",
|
||||||
|
"validuntil": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"nologgedinuser": "",
|
"nologgedinuser": "",
|
||||||
"nologgedinuser_sub": "",
|
"nologgedinuser_sub": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"title": ""
|
"title": "",
|
||||||
|
"greeting": "",
|
||||||
|
"intro": "",
|
||||||
|
"copyright": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
|
|||||||
@@ -838,17 +838,24 @@
|
|||||||
"creating": "",
|
"creating": "",
|
||||||
"notconfigured": "",
|
"notconfigured": "",
|
||||||
"notfoundsubtitle": "",
|
"notfoundsubtitle": "",
|
||||||
"notfoundtitle": ""
|
"notfoundtitle": "",
|
||||||
|
"surveycompletetitle": "",
|
||||||
|
"surveycompletesubtitle": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"completedon": "",
|
"completedon": "",
|
||||||
"created_at": ""
|
"created_at": "",
|
||||||
|
"surveyid": "",
|
||||||
|
"validuntil": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"nologgedinuser": "",
|
"nologgedinuser": "",
|
||||||
"nologgedinuser_sub": "",
|
"nologgedinuser_sub": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"title": ""
|
"title": "",
|
||||||
|
"greeting": "",
|
||||||
|
"intro": "",
|
||||||
|
"copyright": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ app.use('/adm', require("./server/routes/adminRoutes"));
|
|||||||
app.use('/tech', require("./server/routes/techRoutes"));
|
app.use('/tech', require("./server/routes/techRoutes"));
|
||||||
app.use('/intellipay', require("./server/routes/intellipayRoutes"));
|
app.use('/intellipay', require("./server/routes/intellipayRoutes"));
|
||||||
app.use('/cdk', require("./server/routes/cdkRoutes"));
|
app.use('/cdk', require("./server/routes/cdkRoutes"));
|
||||||
|
app.use('/csi', require("./server/routes/csiRoutes"));
|
||||||
|
|
||||||
// Default route for forbidden access
|
// Default route for forbidden access
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
|
|||||||
2
server/csi/csi.js
Normal file
2
server/csi/csi.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
exports.lookup = require("./lookup").default;
|
||||||
|
exports.submit = require("./submit").default;
|
||||||
24
server/csi/lookup.js
Normal file
24
server/csi/lookup.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
29
server/csi/submit.js
Normal file
29
server/csi/submit.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2156,3 +2156,23 @@ exports.ACTIVE_SHOP_BY_USER = `query ACTIVE_SHOP_BY_USER($user: String) {
|
|||||||
shopid
|
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
|
||||||
|
}
|
||||||
|
}`;
|
||||||
8
server/routes/csiRoutes.js
Normal file
8
server/routes/csiRoutes.js
Normal file
@@ -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;
|
||||||
Reference in New Issue
Block a user