Skip to content

Commit

Permalink
Merge pull request #59 from glific/feature/label-send-msg
Browse files Browse the repository at this point in the history
Added label in send message and interactive message node
  • Loading branch information
mdshamoon authored Oct 20, 2021
2 parents 57e1d1e + 62a8173 commit eaf1a3d
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 6 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@glific/flow-editor",
"license": "AGPL-3.0",
"repository": "git://github.com/glific/floweditor.git",
"version": "1.14.0-2",
"version": "1.14.0-3",
"description": "'Standalone flow editing tool designed for use within the Glific suite of messaging tools'",
"browser": "umd/flow-editor.min.js",
"unpkg": "umd/flow-editor.min.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import * as React from 'react';

import i18n from 'config/i18n';
import { getMsgBody } from './helpers';
import { renderAssetList } from '../helpers';
import { AssetType } from 'store/flowContext';
import { MAX_TO_SHOW } from '../addlabels/AddLabels';

export const PLACEHOLDER = i18n.t(
'actions.send_msg.placeholder',
Expand All @@ -14,13 +17,37 @@ const SendInteractiveMsgComp: React.SFC<SendInteractiveMsg> = (
): JSX.Element => {
const message = JSON.parse(action.text);
const body = getMsgBody(message);
const endpoints: any = {};
let labels = null;

if (action.labels) {
labels = renderAssetList(
action.labels.map((label: any) => {
if (label.name_match) {
return {
id: label.name_match,
name: label.name_match,
type: AssetType.NameMatch
};
}
return {
id: label.uuid,
name: label.name,
type: AssetType.Label
};
}),
MAX_TO_SHOW,
endpoints
);
}
if (action.name) {
return (
<div>
<div>
<strong>{action.name}</strong>
</div>
<div>{body}</div>
{labels}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ import styles from './SendInteractiveMsg.module.scss';
import i18n from 'config/i18n';

import AssetSelector from 'components/form/assetselector/AssetSelector';
import { Asset } from 'store/flowContext';
import { AddLabelsFormState } from '../addlabels/AddLabelsForm';

export interface SendInteractiveMsgFormState extends FormState {
interactives: FormEntry;
labels?: any;
}

export default class SendMsgForm extends React.Component<
Expand Down Expand Up @@ -77,6 +80,27 @@ export default class SendMsgForm extends React.Component<
return this.handleUpdate({ text: message }, submitting);
}

public handleLabelsChanged(selected: Asset[], submitting: boolean = false): boolean {
const updates: Partial<AddLabelsFormState> = {
labels: validate(i18n.t('forms.labels', 'Labels'), selected, [shouldRequireIf(submitting)])
};

const updated = mergeForm(this.state, updates);
this.setState(updated);
return updated.valid;
}

public handleCreateAssetFromInput(input: string): any {
return { name: input };
}

public handleLabelCreated(label: Asset): void {
// update our store with our new group
this.props.addAsset('labels', label);

this.handleLabelsChanged(this.state.labels.value!.concat(label));
}

private handleSave(): void {
// don't continue if our message already has errors
if (hasErrors(this.state.interactives)) {
Expand Down Expand Up @@ -104,6 +128,31 @@ export default class SendMsgForm extends React.Component<
};
}

private renderLabelOption(): JSX.Element {
return (
<div className={styles.label_container}>
<p>Select the labels to apply to the interactive message.</p>

<AssetSelector
name={i18n.t('forms.labels', 'Labels')}
placeholder={i18n.t(
'enter_to_create_label',
'Enter the name of an existing label or create a new one'
)}
assets={this.props.assetStore.labels}
entry={this.state.labels}
searchable={true}
multi={true}
expressions={true}
onChange={this.handleLabelsChanged}
createPrefix={i18n.t('create_label', 'Create Label') + ': '}
createAssetFromInput={this.handleCreateAssetFromInput}
onAssetCreated={this.handleLabelCreated}
/>
</div>
);
}

public render(): JSX.Element {
const typeConfig = this.props.typeConfig;

Expand Down Expand Up @@ -132,6 +181,7 @@ export default class SendMsgForm extends React.Component<
formClearable={true}
/>
<div className={styles.body}> {body}</div>
{this.renderLabelOption()}
</Dialog>
);
}
Expand Down
22 changes: 21 additions & 1 deletion src/components/flow/actions/sendinteractivemsg/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { getActionUUID } from 'components/flow/actions/helpers';
import { Types } from 'config/interfaces';
import * as React from 'react';
import { SendInteractiveMsg } from 'flowTypes';
import { Label, SendInteractiveMsg } from 'flowTypes';
import { AssetStore } from 'store/flowContext';
import { NodeEditorSettings } from 'store/nodeEditor';
import styles from './SendInteractiveMsg.module.scss';
Expand All @@ -18,14 +18,28 @@ export const initializeForm = (
const action = settings.originalAction as SendInteractiveMsg;
let { id, text, name } = action;
text = JSON.parse(text);
const labels = action.labels
? action.labels.map((label: Label) => {
if (label.name_match) {
return { name: label.name_match, expression: true };
}
return label;
})
: [];
return {
interactives: { value: { id, interactive_content: text, name } },
labels: {
value: labels
},
valid: true
};
}

return {
interactives: { value: '' },
labels: {
value: []
},
valid: false
};
};
Expand All @@ -38,6 +52,12 @@ export const stateToAction = (
id: state.interactives.value.id,
text: JSON.stringify(state.interactives.value.interactive_content),
name: state.interactives.value.name,
labels: state.labels.value.map((label: any) => {
if (label.expression) {
return { name_match: label.name };
}
return label;
}),
type: Types.send_interactive_msg,
uuid: getActionUUID(settings, Types.send_interactive_msg)
};
Expand Down
41 changes: 39 additions & 2 deletions src/components/flow/actions/sendmsg/SendMsg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,36 @@ import * as React from 'react';

import styles from './SendMsg.module.scss';
import i18n from 'config/i18n';
import { renderAssetList } from '../helpers';
import { AssetType } from 'store/flowContext';
import { MAX_TO_SHOW } from '../addlabels/AddLabels';

export const PLACEHOLDER = i18n.t('actions.send_msg.placeholder', 'Send a message to the contact');

const SendMsgComp: React.SFC<SendMsg> = (action: SendMsg): JSX.Element => {
const endpoints: any = {};
let labels = null;

if (action.labels) {
labels = renderAssetList(
action.labels.map((label: any) => {
if (label.name_match) {
return {
id: label.name_match,
name: label.name_match,
type: AssetType.NameMatch
};
}
return {
id: label.uuid,
name: label.name,
type: AssetType.Label
};
}),
MAX_TO_SHOW,
endpoints
);
}
if (action.text) {
let replies = null;

Expand Down Expand Up @@ -43,16 +69,27 @@ const SendMsgComp: React.SFC<SendMsg> = (action: SendMsg): JSX.Element => {
<div className={`${styles.whatsapp} fe-whatsapp`} />
) : null}
{action.topic ? <div className={`${styles.facebook} fe-facebook`} /> : null}
{labels}
</div>
<div className={styles.summary}>{replies}</div>
</>
);
}
if (action.attachments && action.attachments.length > 0) {
return <div className={`${styles.attachment} fe-paperclip`} />;
return (
<>
<div className={`${styles.attachment} fe-paperclip`} />
{labels}
</>
);
}
if (action.templating && action.templating.template) {
return <div className={`${styles.whatsapp} fe-whatsapp`} />;
return (
<>
<div className={`${styles.whatsapp} fe-whatsapp`} />
{labels}
</>
);
}
return <div className="placeholder">{PLACEHOLDER}</div>;
};
Expand Down
4 changes: 4 additions & 0 deletions src/components/flow/actions/sendmsg/SendMsgForm.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@
temba-completion {
--textarea-height: 120px;
}

.label_container {
margin-top: 16px;
}
50 changes: 50 additions & 0 deletions src/components/flow/actions/sendmsg/SendMsgForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { FeatureFilter } from 'config/interfaces';
import i18n from 'config/i18n';
import { Trans } from 'react-i18next';
import { Attachment, renderAttachments, validateURL } from './attachments';
import { AddLabelsFormState } from '../addlabels/AddLabelsForm';

export interface SendMsgFormState extends FormState {
message: StringEntry;
Expand All @@ -56,6 +57,7 @@ export interface SendMsgFormState extends FormState {
topic: SelectOptionEntry;
templateVariables: StringEntry[];
templateTranslation?: TemplateTranslation;
labels?: any;
}

export default class SendMsgForm extends React.Component<ActionFormProps, SendMsgFormState> {
Expand Down Expand Up @@ -255,6 +257,31 @@ export default class SendMsgForm extends React.Component<ActionFormProps, SendMs
this.setState({ topic: { value: topic } });
}

private renderLabelOption(): JSX.Element {
return (
<div className={styles.label_container}>
<p>Select the labels to apply to the outgoing message.</p>

<AssetSelector
name={i18n.t('forms.labels', 'Labels')}
placeholder={i18n.t(
'enter_to_create_label',
'Enter the name of an existing label or create a new one'
)}
assets={this.props.assetStore.labels}
entry={this.state.labels}
searchable={true}
multi={true}
expressions={true}
onChange={this.handleLabelsChanged}
createPrefix={i18n.t('create_label', 'Create Label') + ': '}
createAssetFromInput={this.handleCreateAssetFromInput}
onAssetCreated={this.handleLabelCreated}
/>
</div>
);
}

private renderTemplateConfig(): JSX.Element {
return (
<>
Expand Down Expand Up @@ -299,6 +326,7 @@ export default class SendMsgForm extends React.Component<ActionFormProps, SendMs
})}
</>
) : null}
{this.renderLabelOption()}
</>
);
}
Expand Down Expand Up @@ -346,13 +374,34 @@ export default class SendMsgForm extends React.Component<ActionFormProps, SendMs
this.setState({ attachments });
}

public handleLabelsChanged(selected: Asset[], submitting: boolean = false): boolean {
const updates: Partial<AddLabelsFormState> = {
labels: validate(i18n.t('forms.labels', 'Labels'), selected, [shouldRequireIf(submitting)])
};

const updated = mergeForm(this.state, updates);
this.setState(updated);
return updated.valid;
}

private handleAttachmentRemoved(index: number) {
const attachments: any = mutate(this.state.attachments, {
$splice: [[index, 1]]
});
this.setState({ attachments });
}

public handleCreateAssetFromInput(input: string): any {
return { name: input };
}

public handleLabelCreated(label: Asset): void {
// update our store with our new group
this.props.addAsset('labels', label);

this.handleLabelsChanged(this.state.labels.value!.concat(label));
}

public render(): JSX.Element {
const typeConfig = this.props.typeConfig;

Expand Down Expand Up @@ -401,6 +450,7 @@ export default class SendMsgForm extends React.Component<ActionFormProps, SendMs
textarea={true}
/>
<temba-charcount class="sms-counter"></temba-charcount>
{this.renderLabelOption()}
{renderIssues(this.props)}
</Dialog>
);
Expand Down
Loading

0 comments on commit eaf1a3d

Please sign in to comment.