Merged in feature/IO-3182-Phone-Number-Consent (pull request #2350)
Feature/IO-3182 Phone Number Consent
This commit is contained in:
@@ -12791,27 +12791,6 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>allow_text_message</name>
|
|
||||||
<definition_loaded>false</definition_loaded>
|
|
||||||
<description></description>
|
|
||||||
<comment></comment>
|
|
||||||
<default_text></default_text>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>es-MX</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-CA</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>checklist</name>
|
<name>checklist</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -42614,27 +42593,6 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>allow_text_message</name>
|
|
||||||
<definition_loaded>false</definition_loaded>
|
|
||||||
<description></description>
|
|
||||||
<comment></comment>
|
|
||||||
<default_text></default_text>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>es-MX</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-CA</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>name</name>
|
<name>name</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -129,24 +129,6 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === "intake" && job.owner && job.owner.id) {
|
|
||||||
//Updae Owner Allow to Text
|
|
||||||
const updateOwnerResult = await updateOwner({
|
|
||||||
variables: {
|
|
||||||
ownerId: job.owner.id,
|
|
||||||
owner: { allow_text_message: values.allow_text_message }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!!updateOwnerResult.errors) {
|
|
||||||
notification["error"]({
|
|
||||||
message: t("checklist.errors.complete", {
|
|
||||||
error: JSON.stringify(result.errors)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
if (!!!result.errors) {
|
if (!!!result.errors) {
|
||||||
@@ -189,7 +171,6 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
|
|||||||
initialValues={{
|
initialValues={{
|
||||||
...(type === "intake" && {
|
...(type === "intake" && {
|
||||||
addToProduction: true,
|
addToProduction: true,
|
||||||
allow_text_message: job.owner && job.owner.allow_text_message,
|
|
||||||
scheduled_completion:
|
scheduled_completion:
|
||||||
(job && job.scheduled_completion && dayjs(job.scheduled_completion)) ||
|
(job && job.scheduled_completion && dayjs(job.scheduled_completion)) ||
|
||||||
(job &&
|
(job &&
|
||||||
@@ -228,14 +209,6 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
|
|||||||
>
|
>
|
||||||
<Switch disabled={readOnly} />
|
<Switch disabled={readOnly} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
name="allow_text_message"
|
|
||||||
valuePropName="checked"
|
|
||||||
label={t("checklist.labels.allow_text_message")}
|
|
||||||
disabled={readOnly}
|
|
||||||
>
|
|
||||||
<Switch disabled={readOnly} />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="scheduled_completion"
|
name="scheduled_completion"
|
||||||
label={t("jobs.fields.scheduled_completion")}
|
label={t("jobs.fields.scheduled_completion")}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Form, Input, Switch } from "antd";
|
import { Form, Input } from "antd";
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||||
@@ -129,13 +129,6 @@ export default function JobsCreateOwnerInfoNewComponent() {
|
|||||||
<Form.Item label={t("owners.fields.preferred_contact")} name={["owner", "data", "preferred_contact"]}>
|
<Form.Item label={t("owners.fields.preferred_contact")} name={["owner", "data", "preferred_contact"]}>
|
||||||
<Input disabled={!state.owner.new} />
|
<Input disabled={!state.owner.new} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("owners.fields.allow_text_message")}
|
|
||||||
valuePropName="checked"
|
|
||||||
name={["owner", "data", "allow_text_message"]}
|
|
||||||
>
|
|
||||||
<Switch disabled={!state.owner.new} />
|
|
||||||
</Form.Item>
|
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Form, Input, Switch } from "antd";
|
import { Form, Input } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
@@ -26,7 +26,7 @@ export default function OwnerDetailFormComponent({ form, loading }) {
|
|||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("owners.fields.accountingid")} name="accountingid">
|
<Form.Item label={t("owners.fields.accountingid")} name="accountingid">
|
||||||
<Input disabled/>
|
<Input disabled />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("owners.forms.address")}>
|
<LayoutFormRow header={t("owners.forms.address")}>
|
||||||
@@ -50,9 +50,6 @@ export default function OwnerDetailFormComponent({ form, loading }) {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("owners.forms.contact")}>
|
<LayoutFormRow header={t("owners.forms.contact")}>
|
||||||
<Form.Item label={t("owners.fields.allow_text_message")} name="allow_text_message" valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("owners.fields.ownr_ea")}
|
label={t("owners.fields.ownr_ea")}
|
||||||
name="ownr_ea"
|
name="ownr_ea"
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Input, Table } from "antd";
|
import { Input, Space, Table, Typography } from "antd";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import { GET_PHONE_NUMBER_OPT_OUTS, SEARCH_OWNERS_BY_PHONE_NUMBERS } from "../../graphql/phone-number-opt-out.queries";
|
import { GET_PHONE_NUMBER_OPT_OUTS, SEARCH_OWNERS_BY_PHONE_NUMBERS } from "../../graphql/phone-number-opt-out.queries";
|
||||||
|
|
||||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||||
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
|
const { Paragraph } = Typography; // Destructure Paragraph from Typography
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -44,18 +47,6 @@ function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
|||||||
fetchPolicy: "network-only"
|
fetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Format owner names for display
|
|
||||||
const formatOwnerName = (owner) => {
|
|
||||||
const parts = [];
|
|
||||||
if (owner.ownr_fn || owner.ownr_ln) {
|
|
||||||
parts.push([owner.ownr_fn, owner.ownr_ln].filter(Boolean).join(" "));
|
|
||||||
}
|
|
||||||
if (owner.ownr_co_nm) {
|
|
||||||
parts.push(owner.ownr_co_nm);
|
|
||||||
}
|
|
||||||
return parts.join(", ") || "-";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Map phone numbers to their associated owners and identify phone field
|
// Map phone numbers to their associated owners and identify phone field
|
||||||
const getAssociatedOwners = (phoneNumber) => {
|
const getAssociatedOwners = (phoneNumber) => {
|
||||||
if (!ownersData?.owners) return [];
|
if (!ownersData?.owners) return [];
|
||||||
@@ -102,15 +93,20 @@ function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
|||||||
}
|
}
|
||||||
return owners.map((owner) => (
|
return owners.map((owner) => (
|
||||||
<div key={owner.id}>
|
<div key={owner.id}>
|
||||||
{formatOwnerName(owner)} ({owner.phoneField})
|
<Space direction="horizontal">
|
||||||
|
<Link to={"/manage/owners/" + owner.id}>
|
||||||
|
<OwnerNameDisplay ownerObject={owner} />
|
||||||
|
</Link>
|
||||||
|
({owner.phoneField})
|
||||||
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
sorter: (a, b) => {
|
sorter: (a, b) => {
|
||||||
const aOwners = getAssociatedOwners(a.phone_number);
|
const aOwners = getAssociatedOwners(a.phone_number);
|
||||||
const bOwners = getAssociatedOwners(b.phone_number);
|
const bOwners = getAssociatedOwners(b.phone_number);
|
||||||
const aName = aOwners[0] ? formatOwnerName(aOwners[0]) : "";
|
const aName = aOwners[0] ? `${aOwners[0].ownr_fn} ${aOwners[0].ownr_ln}` : "";
|
||||||
const bName = bOwners[0] ? formatOwnerName(bOwners[0]) : "";
|
const bName = bOwners[0] ? `${bOwners[0].ownr_fn} ${bOwners[0].ownr_ln}` : "";
|
||||||
return aName.localeCompare(bName);
|
return aName.localeCompare(bName);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -124,6 +120,7 @@ function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<Paragraph>{t("consent.text_body")}</Paragraph>
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder={t("general.labels.search")}
|
placeholder={t("general.labels.search")}
|
||||||
onSearch={(value) => setSearch(value)}
|
onSearch={(value) => setSearch(value)}
|
||||||
|
|||||||
@@ -312,7 +312,6 @@ export const QUERY_INTAKE_CHECKLIST = gql`
|
|||||||
intakechecklist
|
intakechecklist
|
||||||
status
|
status
|
||||||
owner {
|
owner {
|
||||||
allow_text_message
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
labhrs: joblines_aggregate(where: { _and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }] }) {
|
labhrs: joblines_aggregate(where: { _and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }] }) {
|
||||||
|
|||||||
@@ -874,7 +874,6 @@ export const QUERY_JOB_CARD_DETAILS = gql`
|
|||||||
}
|
}
|
||||||
owner {
|
owner {
|
||||||
id
|
id
|
||||||
allow_text_message
|
|
||||||
preferred_contact
|
preferred_contact
|
||||||
tax_number
|
tax_number
|
||||||
}
|
}
|
||||||
@@ -2071,7 +2070,6 @@ export const QUERY_JOB_CHECKLISTS = gql`
|
|||||||
production_vars
|
production_vars
|
||||||
owner {
|
owner {
|
||||||
id
|
id
|
||||||
allow_text_message
|
|
||||||
}
|
}
|
||||||
bodyshop {
|
bodyshop {
|
||||||
id
|
id
|
||||||
@@ -2428,7 +2426,6 @@ export const QUERY_PARTS_QUEUE_CARD_DETAILS = gql`
|
|||||||
ownr_ph2
|
ownr_ph2
|
||||||
owner {
|
owner {
|
||||||
id
|
id
|
||||||
allow_text_message
|
|
||||||
preferred_contact
|
preferred_contact
|
||||||
tax_number
|
tax_number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ export const QUERY_OWNER_BY_ID = gql`
|
|||||||
owners_by_pk(id: $id) {
|
owners_by_pk(id: $id) {
|
||||||
id
|
id
|
||||||
accountingid
|
accountingid
|
||||||
allow_text_message
|
|
||||||
ownr_addr1
|
ownr_addr1
|
||||||
ownr_addr2
|
ownr_addr2
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
@@ -104,7 +103,6 @@ export const QUERY_ALL_OWNERS = gql`
|
|||||||
query QUERY_ALL_OWNERS {
|
query QUERY_ALL_OWNERS {
|
||||||
owners {
|
owners {
|
||||||
id
|
id
|
||||||
allow_text_message
|
|
||||||
created_at
|
created_at
|
||||||
ownr_addr1
|
ownr_addr1
|
||||||
ownr_addr2
|
ownr_addr2
|
||||||
@@ -129,7 +127,6 @@ export const QUERY_ALL_OWNERS_PAGINATED = gql`
|
|||||||
query QUERY_ALL_OWNERS_PAGINATED($search: String, $offset: Int, $limit: Int, $order: [owners_order_by!]!) {
|
query QUERY_ALL_OWNERS_PAGINATED($search: String, $offset: Int, $limit: Int, $order: [owners_order_by!]!) {
|
||||||
search_owners(args: { search: $search }, offset: $offset, limit: $limit, order_by: $order) {
|
search_owners(args: { search: $search }, offset: $offset, limit: $limit, order_by: $order) {
|
||||||
id
|
id
|
||||||
allow_text_message
|
|
||||||
created_at
|
created_at
|
||||||
ownr_addr1
|
ownr_addr1
|
||||||
ownr_addr2
|
ownr_addr2
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
if (!!!job.ownerid) {
|
if (!!!job.ownerid) {
|
||||||
ownerData = job.owner.data;
|
ownerData = job.owner.data;
|
||||||
ownerData.shopid = bodyshop.id;
|
ownerData.shopid = bodyshop.id;
|
||||||
delete ownerData.allow_text_message;
|
|
||||||
delete ownerData.preferred_contact;
|
delete ownerData.preferred_contact;
|
||||||
delete job.ownerid;
|
delete job.ownerid;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -92,13 +92,15 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Consent Settings tab
|
if (bodyshop.messagingservicesid) {
|
||||||
items.push({
|
// Add Consent Settings tab
|
||||||
key: "consent",
|
items.push({
|
||||||
label: t("bodyshop.labels.consent_settings"),
|
key: "consent",
|
||||||
children: <ShopInfoConsentComponent bodyshop={bodyshop} />
|
label: t("bodyshop.labels.consent_settings"),
|
||||||
});
|
children: <ShopInfoConsentComponent bodyshop={bodyshop} />
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RbacWrapper action="shop:config">
|
<RbacWrapper action="shop:config">
|
||||||
<Tabs activeKey={search.tab} onChange={(key) => history({ search: `?tab=${key}` })} items={items} />
|
<Tabs activeKey={search.tab} onChange={(key) => history({ search: `?tab=${key}` })} items={items} />
|
||||||
|
|||||||
@@ -775,7 +775,6 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"addtoproduction": "Add Job to Production?",
|
"addtoproduction": "Add Job to Production?",
|
||||||
"allow_text_message": "Permission to Text?",
|
|
||||||
"checklist": "Checklist",
|
"checklist": "Checklist",
|
||||||
"printpack": "Job Intake Print Pack",
|
"printpack": "Job Intake Print Pack",
|
||||||
"removefromproduction": "Remove Job from Production?"
|
"removefromproduction": "Remove Job from Production?"
|
||||||
@@ -2524,7 +2523,6 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"accountingid": "Accounting ID",
|
"accountingid": "Accounting ID",
|
||||||
"address": "Address",
|
"address": "Address",
|
||||||
"allow_text_message": "Permission to Text?",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"note": "Owner Note",
|
"note": "Owner Note",
|
||||||
"ownr_addr1": "Address",
|
"ownr_addr1": "Address",
|
||||||
@@ -3876,7 +3874,8 @@
|
|||||||
"created_at": "Opt-Out Date",
|
"created_at": "Opt-Out Date",
|
||||||
"no_owners": "No Associated Owners",
|
"no_owners": "No Associated Owners",
|
||||||
"phone_1": "Phone 1",
|
"phone_1": "Phone 1",
|
||||||
"phone_2": "Phone 2"
|
"phone_2": "Phone 2",
|
||||||
|
"text_body": "Users can opt out of receiving SMS messages by replying with keywords such as STOP, UNSUBSCRIBE, CANCEL, END, QUIT, STOPALL, REVOKE and OPTOUT. To opt back in, users can reply with START, YES, or UNSTOP. Even after opting out, users can still send messages to us, which will be received and processed as needed. Ensure customers are informed to reply with these keywords to manage their messaging preferences. After opting out, users receive a confirmation message and will not receive further messages until they opt back in."
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Phone Number Opt-Out List"
|
"title": "Phone Number Opt-Out List"
|
||||||
|
|||||||
@@ -775,7 +775,6 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"addtoproduction": "",
|
"addtoproduction": "",
|
||||||
"allow_text_message": "",
|
|
||||||
"checklist": "",
|
"checklist": "",
|
||||||
"printpack": "",
|
"printpack": "",
|
||||||
"removefromproduction": ""
|
"removefromproduction": ""
|
||||||
@@ -2526,7 +2525,6 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"accountingid": "",
|
"accountingid": "",
|
||||||
"address": "Dirección",
|
"address": "Dirección",
|
||||||
"allow_text_message": "Permiso de texto?",
|
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
"note": "",
|
"note": "",
|
||||||
"ownr_addr1": "Dirección",
|
"ownr_addr1": "Dirección",
|
||||||
@@ -3878,7 +3876,8 @@
|
|||||||
"created_at": "",
|
"created_at": "",
|
||||||
"no_owners": "",
|
"no_owners": "",
|
||||||
"phone_1": "",
|
"phone_1": "",
|
||||||
"phone_2": ""
|
"phone_2": "",
|
||||||
|
"text_body": ""
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": ""
|
"title": ""
|
||||||
|
|||||||
@@ -775,7 +775,6 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"addtoproduction": "",
|
"addtoproduction": "",
|
||||||
"allow_text_message": "",
|
|
||||||
"checklist": "",
|
"checklist": "",
|
||||||
"printpack": "",
|
"printpack": "",
|
||||||
"removefromproduction": ""
|
"removefromproduction": ""
|
||||||
@@ -2526,7 +2525,6 @@
|
|||||||
"fields": {
|
"fields": {
|
||||||
"accountingid": "",
|
"accountingid": "",
|
||||||
"address": "Adresse",
|
"address": "Adresse",
|
||||||
"allow_text_message": "Autorisation de texte?",
|
|
||||||
"name": "Prénom",
|
"name": "Prénom",
|
||||||
"note": "",
|
"note": "",
|
||||||
"ownr_addr1": "Adresse",
|
"ownr_addr1": "Adresse",
|
||||||
@@ -3878,7 +3876,8 @@
|
|||||||
"created_at": "Opt-Out Date",
|
"created_at": "Opt-Out Date",
|
||||||
"no_owners": "No Associated Owners",
|
"no_owners": "No Associated Owners",
|
||||||
"phone_1": "Phone 1",
|
"phone_1": "Phone 1",
|
||||||
"phone_2": "Phone 2"
|
"phone_2": "Phone 2",
|
||||||
|
"text_body": ""
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": ""
|
"title": ""
|
||||||
|
|||||||
@@ -10028,25 +10028,6 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>allow_text_message</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>es-ES</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-CA</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>checklist</name>
|
<name>checklist</name>
|
||||||
<description/>
|
<description/>
|
||||||
@@ -33000,25 +32981,6 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>allow_text_message</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>es-ES</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-CA</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>name</name>
|
<name>name</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ const { phone } = require("phone");
|
|||||||
const { admin } = require("../firebase/firebase-handler");
|
const { admin } = require("../firebase/firebase-handler");
|
||||||
const InstanceManager = require("../utils/instanceMgr").default;
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
|
|
||||||
|
// Note: When we handle different languages, we might need to adjust these keywords accordingly.
|
||||||
|
const optInKeywords = ["START", "YES", "UNSTOP"];
|
||||||
|
const optOutKeywords = ["STOP", "STOPALL", "UNSUBSCRIBE", "CANCEL", "END", "QUIT", "REVOKE", "OPTOUT"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive SMS messages from Twilio and process them
|
* Receive SMS messages from Twilio and process them
|
||||||
* @param req
|
* @param req
|
||||||
@@ -58,9 +62,6 @@ const receive = async (req, res) => {
|
|||||||
const messageText = (req.body.Body || "").trim().toUpperCase();
|
const messageText = (req.body.Body || "").trim().toUpperCase();
|
||||||
|
|
||||||
// Step 2: Check for opt-in or opt-out keywords
|
// Step 2: Check for opt-in or opt-out keywords
|
||||||
const optInKeywords = ["START", "YES", "UNSTOP"];
|
|
||||||
const optOutKeywords = ["STOP", "STOPALL", "UNSUBSCRIBE", "CANCEL", "END", "QUIT"];
|
|
||||||
|
|
||||||
if (optInKeywords.includes(messageText) || optOutKeywords.includes(messageText)) {
|
if (optInKeywords.includes(messageText) || optOutKeywords.includes(messageText)) {
|
||||||
// Check if the phone number is in phone_number_opt_out
|
// Check if the phone number is in phone_number_opt_out
|
||||||
const optOutCheck = await client.request(CHECK_PHONE_NUMBER_OPT_OUT, {
|
const optOutCheck = await client.request(CHECK_PHONE_NUMBER_OPT_OUT, {
|
||||||
|
|||||||
Reference in New Issue
Block a user