Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to also suppress Tags in Message Review #1967

Merged
merged 1 commit into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/api/campaign-contact.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const schema = `
validTimezone: Boolean
includePastDue: Boolean
tags: [String]
suppressedTags: [String]
contactId: String
errorCode: [Int]
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/IncomingMessageFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class IncomingMessageFilter extends Component {
return left.text.localeCompare(right.text, "en", { sensitivity: "base" });
});

//this.state.texterSearchText = this.props.texterSearchText;
// this.state.texterSearchText = this.props.texterSearchText;

return (
<Card>
Expand Down
64 changes: 56 additions & 8 deletions src/components/TagsSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import MenuItem from "material-ui/MenuItem";
import Divider from "material-ui/Divider";
import Humps from "humps";

const inlineStyles = {
sectionLabel: {
margin: "5px 0 0 20px",
fontWeight: "bold",
fontSize: 16
}
};

const NO_TAG = { id: -1, name: "NO TAG" };
const ANY_TAG = { id: -2, name: "ANY TAG" };
const IGNORE_TAGS = { id: -3, name: "IGNORE TAGS" };
Expand All @@ -19,7 +27,8 @@ const makeTagMetafilter = (ignoreTags, anyTag, noTag, tagItem) => {
ignoreTags,
anyTag,
noTag,
selectedTags: {}
selectedTags: {},
suppressedTags: {}
};

if (tagItem) {
Expand Down Expand Up @@ -54,6 +63,7 @@ export class TagsSelector extends React.Component {

cloneTagFilter = () => {
const selectedTags = {};

if (Object.keys(this.props.tagsFilter.selectedTags || {}).length > 0) {
Object.keys(this.props.tagsFilter.selectedTags).forEach(key => {
if (key > 0) {
Expand All @@ -62,11 +72,22 @@ export class TagsSelector extends React.Component {
});
}

const suppressedTags = {};

if (Object.keys(this.props.tagsFilter.suppressedTags || {}).length > 0) {
Object.keys(this.props.tagsFilter.suppressedTags).forEach(key => {
if (key > 0) {
suppressedTags[key] = this.state.tags[key] || TAG_META_FILTERS[key];
}
});
}

return {
ignoreTags: this.props.tagsFilter.ignoreTags,
anyTag: this.props.tagsFilter.anyTag,
noTag: this.props.tagsFilter.noTag,
selectedTags
selectedTags,
suppressedTags
};
};

Expand All @@ -89,8 +110,14 @@ export class TagsSelector extends React.Component {

if (itemClicked.id in tagFilter.selectedTags) {
delete tagFilter.selectedTags[itemClicked.id];
} else if (itemClicked.id in tagFilter.suppressedTags) {
delete tagFilter.suppressedTags[itemClicked.id];
} else if (String(itemClicked.id).startsWith("s_")) {
tagFilter.suppressedTags[itemClicked.id] = itemClicked;
delete tagFilter.selectedTags[itemClicked.id.replace("s_", "")];
} else {
tagFilter.selectedTags[itemClicked.id] = itemClicked;
delete tagFilter.suppressedTags[`s_${itemClicked.id}`];
}
}

Expand Down Expand Up @@ -118,10 +145,10 @@ export class TagsSelector extends React.Component {
];
return this.createMenuItem(tagFilter, isChecked);
});
return [...menuItems, <Divider key={-9999} inset />];
return menuItems;
};

createMenuItems = tagFilters => {
createTagMenuItems = tagFilters => {
return tagFilters
.sort((left, right) => {
if (left.name === right.name) {
Expand All @@ -131,8 +158,11 @@ export class TagsSelector extends React.Component {
return left.name < right.name ? 0 : 1;
})
.map(tagFilter => {
const isChecked =
tagFilter.id in (this.state.tagFilter.selectedTags || []);
const isChecked = [
...Object.keys(this.state.tagFilter.selectedTags || {}),
...Object.keys(this.state.tagFilter.suppressedTags || {})
].includes(tagFilter.id);

return this.createMenuItem(tagFilter, isChecked);
});
};
Expand All @@ -151,7 +181,10 @@ export class TagsSelector extends React.Component {
return [IGNORE_TAGS];
}

return Object.values(tagFilter.selectedTags);
return [
...Object.values(tagFilter.selectedTags),
...Object.values(tagFilter.suppressedTags)
];
};

render = () => (
Expand All @@ -161,9 +194,24 @@ export class TagsSelector extends React.Component {
hintText={this.props.hintText}
floatingLabelText={"Tags"}
floatingLabelFixed
maxHeight={600}
>
{this.createMetaFilterMenuItems(Object.values(TAG_META_FILTERS))}
{this.createMenuItems(Object.values(this.state.tags))}

<Divider inset />
<div style={inlineStyles.sectionLabel}>FILTER BY TAGS</div>

{this.createTagMenuItems(Object.values(this.state.tags))}

<Divider inset />
<div style={inlineStyles.sectionLabel}>SUPPRESS TAGS</div>

{this.createTagMenuItems(
Object.values(this.state.tags).map(tag => ({
...tag,
id: `s_${tag.id}`
}))
)}
</SelectField>
);
}
Expand Down
8 changes: 6 additions & 2 deletions src/containers/AdminIncomingMessageList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ export class AdminIncomingMessageList extends Component {
const selectedTags = Object.keys(
nextState.tagsFilter.selectedTags || {}
).filter(t => t);
query.tags = selectedTags.join(",");
const suppressedTags = Object.keys(
nextState.tagsFilter.suppressedTags || {}
).filter(t => t);
query.tags = [...selectedTags, ...suppressedTags].join(",");
}
}
// default false
Expand Down Expand Up @@ -367,7 +370,8 @@ export class AdminIncomingMessageList extends Component {

const contactsFilter = {
...this.state.contactsFilter,
tags: newTagsFilter || undefined
tags: (newTagsFilter || {}).include || undefined,
suppressedTags: (newTagsFilter || {}).suppress || undefined
};

this.setState({
Expand Down
16 changes: 10 additions & 6 deletions src/lib/conversations.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
export const tagsFilterStateFromTagsFilter = tagsFilter => {
let newTagsFilter = null;
const newTagsFilter = {};
if (tagsFilter.anyTag) {
newTagsFilter = ["*"];
newTagsFilter.include = ["*"];
} else if (tagsFilter.noTag) {
newTagsFilter = [];
newTagsFilter.include = [];
} else if (!tagsFilter.ignoreTags) {
newTagsFilter = Object.values(tagsFilter.selectedTags).map(
tagFilter => tagFilter.id
newTagsFilter.include = Object.values(tagsFilter.selectedTags || {}).map(
tagFilter => (tagFilter || {}).id
);
newTagsFilter.suppress = Object.values(tagsFilter.suppressedTags || {}).map(
tagFilter => (tagFilter || {}).id
);
}
return newTagsFilter;
Expand Down Expand Up @@ -108,6 +111,7 @@ export const getConversationFiltersFromQuery = (query, organizationTags) => {
}
}
const newTagsFilter = tagsFilterStateFromTagsFilter(filters.tagsFilter);
filters.contactsFilter.tags = newTagsFilter;
filters.contactsFilter.tags = newTagsFilter.include;
filters.contactsFilter.suppressedTags = newTagsFilter.suppress;
return filters;
};
18 changes: 18 additions & 0 deletions src/server/api/conversations.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,24 @@ function getConversationsJoinsAndWhereClause(
query = query.whereExists(tagsSubquery);
}
}

if (contactsFilter.suppressedTags) {
const tags = contactsFilter.suppressedTags.map(id =>
id.replace("s_", "")
);

if (tags.length >= 1) {
query = query.whereNotExists(
r.knexReadOnly
.select(1)
.from("tag_campaign_contact")
.whereRaw(
"campaign_contact.id = tag_campaign_contact.campaign_contact_id"
)
.whereIn("tag_id", tags)
);
}
}
}

return query;
Expand Down