diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 1c0e48ea3..9a1af3c1e 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -3915,6 +3915,48 @@ errors + + creating + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + notconfigured + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + notfoundsubtitle false @@ -3988,6 +4030,27 @@ successes + + created + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + submitted false @@ -4976,6 +5039,27 @@ + + email + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + in false @@ -5228,6 +5312,27 @@ + + text + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + unknown false @@ -7311,6 +7416,27 @@ + + sendcsi + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx index 1fa6fb74a..3c325d9b1 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx @@ -10,9 +10,12 @@ import { selectBodyshop } from "../../redux/user/user.selectors"; import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util"; import DuplicateJob from "./jobs-detail-header-actions.duplicate.util"; import { setModalContext } from "../../redux/modals/modals.actions"; +import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component"; + const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, }); + const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) setInvoiceEnterContext: (context) => @@ -29,29 +32,27 @@ export function JobsDetailHeaderActions({ const client = useApolloClient(); const history = useHistory(); const statusmenu = ( - - + + + }}> {t("menus.jobsactions.newcccontract")} AddToProduction(client, job.id, refetch)} - > + onClick={() => AddToProduction(client, job.id, refetch)}> {t("jobs.actions.addtoproduction")} - + e.stopPropagation()} onConfirm={() => DuplicateJob( @@ -63,13 +64,12 @@ export function JobsDetailHeaderActions({ } ) } - getPopupContainer={(trigger) => trigger.parentNode} - > + getPopupContainer={(trigger) => trigger.parentNode}> {t("menus.jobsactions.duplicate")} { setInvoiceEnterContext({ actions: { refetch: refetch }, @@ -77,23 +77,22 @@ export function JobsDetailHeaderActions({ job: job, }, }); - }} - > + }}> {t("jobs.actions.postInvoices")} - + + }}> {t("menus.jobsactions.closejob")} + ); return ( - + diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.csi.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.csi.component.jsx new file mode 100644 index 000000000..0e85d6b37 --- /dev/null +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.csi.component.jsx @@ -0,0 +1,124 @@ +import { useMutation, useApolloClient } from "@apollo/react-hooks"; +import { Menu, notification } from "antd"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { + INSERT_CSI, + GET_CURRENT_QUESTIONSET_ID, +} from "../../graphql/csi.queries"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import { setEmailOptions } from "../../redux/email/email.actions"; +import { TemplateList } from "../../utils/TemplateConstants"; + +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser' + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + setEmailOptions: (e) => dispatch(setEmailOptions(e)), +}); + +export function JobsDetailHeaderCsi({ + setEmailOptions, + bodyshop, + job, + ...props +}) { + const { t } = useTranslation(); + const [insertCsi] = useMutation(INSERT_CSI); + const client = useApolloClient(); + + const handleCreateCsi = async (e) => { + console.log("e.target.key", e.key); + + const questionSetResult = await client.query({ + query: GET_CURRENT_QUESTIONSET_ID, + }); + + if (questionSetResult.data.csiquestions.length > 0) { + const result = await insertCsi({ + variables: { + csiInput: { + jobid: job.id, + bodyshopid: bodyshop.id, + questionset: questionSetResult.data.csiquestions[0].id, + relateddata: { + job: { + id: job.id, + ownr_fn: job.ownr_fn, + ro_number: job.ro_number, + v_model_yr: job.v_model_yr, + v_make_desc: job.v_make_desc, + v_model_desc: job.v_model_desc, + }, + bodyshop: { + city: bodyshop.city, + email: bodyshop.email, + state: bodyshop.state, + country: bodyshop.country, + address1: bodyshop.address1, + address2: bodyshop.address2, + shopname: bodyshop.shopname, + zip_post: bodyshop.zip_post, + logo_img_path: bodyshop.logo_img_path, + }, + }, + }, + }, + }); + + if (!!!result.errors) { + notification["success"]({ message: t("csi.successes.created") }); + } else { + notification["error"]({ + message: t("csi.errors.creating", { + message: JSON.stringify(result.errors), + }), + }); + return; + } + if (e.key === "email") + setEmailOptions({ + messageOptions: { + to: job.ownr_ea, + replyTo: bodyshop.email, + }, + template: { + name: TemplateList.csi_invitation.key, + variables: { + id: result.data.insert_csi.returning[0].id, + }, + }, + }); + + if (e.key === "text") console.log("TODO Handling texting"); //TODO Implement texting. + } else { + notification["error"]({ + message: t("csi.errors.notconfigured"), + }); + } + }; + + return ( + + + {t("general.labels.email")} + + + {t("general.labels.text")} + + + ); +} +export default connect( + mapStateToProps, + mapDispatchToProps +)(JobsDetailHeaderCsi); diff --git a/client/src/graphql/csi.queries.js b/client/src/graphql/csi.queries.js index 3d296c915..08e4b57e4 100644 --- a/client/src/graphql/csi.queries.js +++ b/client/src/graphql/csi.queries.js @@ -21,3 +21,21 @@ export const COMPLETE_SURVEY = gql` } } `; + +export const GET_CURRENT_QUESTIONSET_ID = gql` + query GET_CURRENT_QUESTIONSET_ID { + csiquestions(where: { current: { _eq: true } }) { + id + } + } +`; + +export const INSERT_CSI = gql` + mutation INSERT_CSI($csiInput: [csi_insert_input!]!) { + insert_csi(objects: $csiInput) { + returning { + id + } + } + } +`; diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 47c730d2d..fd0f979e5 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -256,6 +256,8 @@ }, "csi": { "errors": { + "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." }, @@ -263,6 +265,7 @@ "title": "Customer Satisfaction Survey" }, "successes": { + "created": "CSI created successfully. ", "submitted": "Your responses have been submitted successfully.", "submittedsub": "Your input is highly appreciated." } @@ -340,6 +343,7 @@ "actions": "Actions", "areyousure": "Are you sure?", "barcode": "Barcode", + "email": "Email", "in": "In", "instanceconflictext": "Your $t(titles.app) account can only be used on one device at any given time. Refresh your session to take control.", "instanceconflictitle": "Your account is being used elsewhere.", @@ -352,6 +356,7 @@ "out": "Out", "search": "Search...", "selectdate": "Select date...", + "text": "Text", "unknown": "Unknown", "yes": "Yes" }, @@ -496,7 +501,8 @@ "postInvoices": "Post Invoices", "printCenter": "Print Center", "reconcile": "Reconcile", - "schedule": "Schedule" + "schedule": "Schedule", + "sendcsi": "Send CSI" }, "errors": { "addingtoproduction": "Error adding to production. {{error}}", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 8f7d5efdd..4f21aea93 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -256,6 +256,8 @@ }, "csi": { "errors": { + "creating": "", + "notconfigured": "", "notfoundsubtitle": "", "notfoundtitle": "" }, @@ -263,6 +265,7 @@ "title": "" }, "successes": { + "created": "", "submitted": "", "submittedsub": "" } @@ -340,6 +343,7 @@ "actions": "Comportamiento", "areyousure": "", "barcode": "código de barras", + "email": "", "in": "en", "instanceconflictext": "", "instanceconflictitle": "", @@ -352,6 +356,7 @@ "out": "Afuera", "search": "Buscar...", "selectdate": "", + "text": "", "unknown": "Desconocido", "yes": "" }, @@ -496,7 +501,8 @@ "postInvoices": "Contabilizar facturas", "printCenter": "Centro de impresión", "reconcile": "", - "schedule": "Programar" + "schedule": "Programar", + "sendcsi": "" }, "errors": { "addingtoproduction": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 2a5f31046..0005bc80d 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -256,6 +256,8 @@ }, "csi": { "errors": { + "creating": "", + "notconfigured": "", "notfoundsubtitle": "", "notfoundtitle": "" }, @@ -263,6 +265,7 @@ "title": "" }, "successes": { + "created": "", "submitted": "", "submittedsub": "" } @@ -340,6 +343,7 @@ "actions": "actes", "areyousure": "", "barcode": "code à barre", + "email": "", "in": "dans", "instanceconflictext": "", "instanceconflictitle": "", @@ -352,6 +356,7 @@ "out": "En dehors", "search": "Chercher...", "selectdate": "", + "text": "", "unknown": "Inconnu", "yes": "" }, @@ -496,7 +501,8 @@ "postInvoices": "Poster des factures", "printCenter": "Centre d'impression", "reconcile": "", - "schedule": "Programme" + "schedule": "Programme", + "sendcsi": "" }, "errors": { "addingtoproduction": "", diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 6d4032122..0bc5aa69c 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -1,6 +1,6 @@ export const EmailSettings = { - fromNameDefault: "Bodyshop.app", - fromAddress: "noreply@bodyshop.app", + fromNameDefault: "ImEX Online", + fromAddress: "noreply@imex.online", }; export const TemplateList = { @@ -28,4 +28,10 @@ export const TemplateList = { drivingId: "Parts order Id", key: "parts_return_confirmation", }, + csi_invitation: { + title: "Customer Survey Invitation", + description: "Customer Survey Invitation", + drivingId: "csi Id", + key: "csi_invitation", + }, }; diff --git a/client/templates/csi_invitation/csi_invitation.query.gql b/client/templates/csi_invitation/csi_invitation.query.gql new file mode 100644 index 000000000..0dacda99a --- /dev/null +++ b/client/templates/csi_invitation/csi_invitation.query.gql @@ -0,0 +1,6 @@ +query ($id: uuid!){ + csi_by_pk(id: $id){ + id + relateddata + } +} \ No newline at end of file diff --git a/client/templates/csi_invitation/csi_invitation.template.html b/client/templates/csi_invitation/csi_invitation.template.html new file mode 100644 index 000000000..ba8b22f31 --- /dev/null +++ b/client/templates/csi_invitation/csi_invitation.template.html @@ -0,0 +1,45 @@ +
Hi {{csi_by_pk.relateddata.job.ownr_fn}}, 
+
 
+
+ Thank you for getting your car repaired at + {{csi_by_pk.relateddata.bodyshop.shopname}}. We invite you to complete a + survey about your experience.  +
+
 
+ + + + + + +
+ + + + + + +
+ Complete Survey → +
+
+
 
diff --git a/hasura/migrations/1590772155750_update_permission_user_public_table_csi/down.yaml b/hasura/migrations/1590772155750_update_permission_user_public_table_csi/down.yaml new file mode 100644 index 000000000..78940bde7 --- /dev/null +++ b/hasura/migrations/1590772155750_update_permission_user_public_table_csi/down.yaml @@ -0,0 +1,35 @@ +- args: + role: user + table: + name: csi + schema: public + type: drop_insert_permission +- args: + permission: + check: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + columns: + - bodyshopid + - created_at + - id + - jobid + - relateddata + - updated_at + - valid + - validuntil + localPresets: + - key: "" + value: "" + set: {} + role: user + table: + name: csi + schema: public + type: create_insert_permission diff --git a/hasura/migrations/1590772155750_update_permission_user_public_table_csi/up.yaml b/hasura/migrations/1590772155750_update_permission_user_public_table_csi/up.yaml new file mode 100644 index 000000000..a19f5219a --- /dev/null +++ b/hasura/migrations/1590772155750_update_permission_user_public_table_csi/up.yaml @@ -0,0 +1,36 @@ +- args: + role: user + table: + name: csi + schema: public + type: drop_insert_permission +- args: + permission: + check: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + columns: + - bodyshopid + - created_at + - id + - jobid + - questionset + - relateddata + - updated_at + - valid + - validuntil + localPresets: + - key: "" + value: "" + set: {} + role: user + table: + name: csi + schema: public + type: create_insert_permission diff --git a/hasura/migrations/1590772289591_update_permission_user_public_table_csi/down.yaml b/hasura/migrations/1590772289591_update_permission_user_public_table_csi/down.yaml new file mode 100644 index 000000000..bc9bab34b --- /dev/null +++ b/hasura/migrations/1590772289591_update_permission_user_public_table_csi/down.yaml @@ -0,0 +1,35 @@ +- args: + role: user + table: + name: csi + schema: public + type: drop_update_permission +- args: + permission: + columns: + - valid + - validuntil + - relateddata + - created_at + - updated_at + - bodyshopid + - id + - jobid + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + localPresets: + - key: "" + value: "" + set: {} + role: user + table: + name: csi + schema: public + type: create_update_permission diff --git a/hasura/migrations/1590772289591_update_permission_user_public_table_csi/up.yaml b/hasura/migrations/1590772289591_update_permission_user_public_table_csi/up.yaml new file mode 100644 index 000000000..7820d79cc --- /dev/null +++ b/hasura/migrations/1590772289591_update_permission_user_public_table_csi/up.yaml @@ -0,0 +1,36 @@ +- args: + role: user + table: + name: csi + schema: public + type: drop_update_permission +- args: + permission: + columns: + - bodyshopid + - completedon + - created_at + - id + - jobid + - relateddata + - updated_at + - valid + - validuntil + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + localPresets: + - key: "" + value: "" + set: {} + role: user + table: + name: csi + schema: public + type: create_update_permission diff --git a/hasura/migrations/metadata.yaml b/hasura/migrations/metadata.yaml index 09a8b4d0f..6ef734a5c 100644 --- a/hasura/migrations/metadata.yaml +++ b/hasura/migrations/metadata.yaml @@ -913,6 +913,7 @@ tables: - created_at - id - jobid + - questionset - relateddata - updated_at - valid @@ -965,14 +966,15 @@ tables: - role: user permission: columns: - - valid - - validuntil - - relateddata - - created_at - - updated_at - bodyshopid + - completedon + - created_at - id - jobid + - relateddata + - updated_at + - valid + - validuntil filter: bodyshop: associations: