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

Automation drafts feature added #671

Merged
merged 11 commits into from
Nov 5, 2020
38 changes: 29 additions & 9 deletions src/components/floweditor/FlowEditor.module.css
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
.Button {
position: fixed !important;
bottom: 20px;
right: 15px;
z-index: 1000;
border: 0px !important;
background-color: white !important;
}

.Link {
text-decoration: none;
display: flex;
}

.HelpIcon {
position: fixed !important;
bottom: 25px;
right: 130px;
z-index: 1000;
width: 28px !important;
height: 28px;
margin-right: 10px;
}

.AutomationName {
Expand Down Expand Up @@ -46,3 +40,29 @@
.FlowContainer {
position: relative;
}

.ButtonContainer {
display: flex;
position: fixed !important;
align-items: center;
bottom: 12px;
right: 20px;
z-index: 1000;
}

.DialogDescription {
text-align: center;
margin: 0;
font-size: 14px;
font-weight: 400;
color: #073f24;
}

.CrossIcon{
margin-left: 15px;
color: grey;
}

.ContainedButton{
border: unset !important;
}
21 changes: 18 additions & 3 deletions src/components/floweditor/FlowEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { FlowEditor } from './FlowEditor';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import { wait } from '@testing-library/react';
import { getAutomationNameQuery } from '../../mocks/Automation';
import { getAutomationDetailsQuery } from '../../mocks/Automation';
import { conversationQuery } from '../../mocks/Chat';

const mocks = [getAutomationNameQuery];
const mocks = [getAutomationDetailsQuery,conversationQuery];
const wrapper = mount(
<MockedProvider mocks={mocks} addTypename={false}>
<MemoryRouter>
Expand All @@ -31,5 +32,19 @@ test('it should display name of the automation', async () => {

test('it should have a help button that redirects to help page', () => {
expect(wrapper.find('[data-testid="helpButton"]').exists()).toBe(true);
wrapper.unmount();

});

test('it should have a preview button', () => {
expect(wrapper.find('[data-testid="previewButton"]').exists()).toBe(true);
});

test('it should have save as draft button', () => {
expect(wrapper.find('[data-testid="saveDraftButton"]').exists()).toBe(true);
});

test('click on preview button should open simulator', () => {
wrapper.find('button[data-testid="previewButton"]').simulate('click');
expect( wrapper.find('[data-testid="beneficiaryName"]').text()).toBe('Beneficiary')

});
139 changes: 113 additions & 26 deletions src/components/floweditor/FlowEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { useEffect, useState } from 'react';
import { useMutation, useQuery } from '@apollo/client';
import { useApolloClient, useMutation, useQuery } from '@apollo/client';
import { Prompt, Redirect, useHistory } from 'react-router';

import CancelOutlinedIcon from '@material-ui/icons/CancelOutlined';
import styles from './FlowEditor.module.css';
import { ReactComponent as HelpIcon } from '../../assets/images/icons/Help.svg';
import { Button } from '../UI/Form/Button/Button';
import { PUBLISH_AUTOMATION } from '../../graphql/mutations/Automation';
import { APP_NAME, FLOW_EDITOR_CONFIGURE_LINK } from '../../config/index';
import { FLOW_EDITOR_API } from '../../config/index';
import * as Manifest from '@nyaruka/flow-editor/build/asset-manifest.json';
import { GET_AUTOMATION_NAME } from '../../graphql/queries/Automation';
import { GET_AUTOMATION_DETAILS } from '../../graphql/queries/Automation';
import { ReactComponent as AutomationIcon } from '../../assets/images/icons/Automations/Dark.svg';
import { IconButton } from '@material-ui/core';
import { Simulator } from '../simulator/Simulator';
import { DialogBox } from '../UI/DialogBox/DialogBox';
import { setNotification } from '../../common/notification';

declare function showFlowEditor(node: any, config: any): void;

Expand Down Expand Up @@ -143,11 +145,19 @@ export interface FlowEditorProps {
}

export const FlowEditor = (props: FlowEditorProps) => {
const uuid = props.match.params.uuid;
const client = useApolloClient();
const history = useHistory();
const uuid = props.match.params.uuid;
const [publishDialog, setPublishDialog] = useState(false);
const [showSimulator, setShowSimulator] = useState(false);
const config = setConfig(uuid);
const [publishFlow] = useMutation(PUBLISH_AUTOMATION);
const [publishFlow] = useMutation(PUBLISH_AUTOMATION, {
onCompleted: () => {
setNotification(client, 'The flow has been published');
},
});
const [published, setPublished] = useState(false);
let dialog = null;
const [modalVisible, setModalVisible] = useState(false);
const [lastLocation, setLastLocation] = useState<Location | null>(null);
const [confirmedNavigation, setConfirmedNavigation] = useState(false);
Expand Down Expand Up @@ -187,7 +197,7 @@ export const FlowEditor = (props: FlowEditorProps) => {
);
}

const { data: automationName } = useQuery(GET_AUTOMATION_NAME, {
const { data: automationName } = useQuery(GET_AUTOMATION_DETAILS, {
variables: {
filter: {
uuid: uuid,
Expand All @@ -196,9 +206,16 @@ export const FlowEditor = (props: FlowEditorProps) => {
},
});

let automationTitle: any;
let automationKeyword: any;
if (automationName) {
automationTitle = automationName.flows[0].name;
automationKeyword = automationName.flows[0].keywords[0];
}

useEffect(() => {
if (automationName) {
document.title = automationName.flows[0].name;
document.title = automationTitle;
}
return () => {
document.title = APP_NAME;
Expand Down Expand Up @@ -228,32 +245,102 @@ export const FlowEditor = (props: FlowEditorProps) => {
setPublished(true);
};

if (publishDialog) {
dialog = (
<DialogBox
title="Are you ready to publish the flow?"
buttonOk="Publish"
handleOk={() => handlePublishFlow()}
handleCancel={() => setPublishDialog(false)}
alignButtons="center"
>
<p className={styles.DialogDescription}>New changes will be activated for the users</p>
</DialogBox>
);
}

if (published) {
return <Redirect to="/automation" />;
}

return (
<>
{dialog}
<div className={styles.ButtonContainer}>
<a
href="https://app.rapidpro.io/video/"
className={styles.Link}
target="_blank"
rel="noopener noreferrer"
data-testid="helpButton"
>
<HelpIcon className={styles.HelpIcon} />
</a>

<Button
variant="contained"
color="default"
className={styles.ContainedButton}
onClick={() => {
setConfirmedNavigation(true);
history.push('/automation');
}}
>
Back
</Button>

<Button
variant="outlined"
color="primary"
data-testid='saveDraftButton'
className={styles.Button}
onClick={() => {
setConfirmedNavigation(true);
setNotification(client, 'The flow has been saved as draft');
history.push('/automation');
}}
>
Save as draft
</Button>
<Button
variant="outlined"
color="primary"
data-testid="previewButton"
className={styles.Button}
onClick={() => {
setShowSimulator(!showSimulator);
}}
>
Preview
{showSimulator ? (
<CancelOutlinedIcon
className={styles.CrossIcon}
onClick={() => {
setShowSimulator(false);
}}
/>
) : null}
</Button>
<Button
variant="contained"
color="primary"
data-testid="button"
className={styles.ContainedButton}
onClick={() => setPublishDialog(true)}
>
Publish
</Button>
</div>
{showSimulator ? (
<Simulator
showSimulator={showSimulator}
setShowSimulator={setShowSimulator}
message={{ type: 'draft', keyword: automationKeyword }}
/>
) : null}
{modal}
<Prompt when={true} message={handleBlockedNavigation} />
<a
href="https://app.rapidpro.io/video/"
className={styles.Link}
target="_blank"
rel="noopener noreferrer"
data-testid="helpButton"
>
<HelpIcon className={styles.HelpIcon} />
</a>
<Button
variant="contained"
color="primary"
className={styles.Button}
data-testid="button"
onClick={handlePublishFlow}
>
Update
</Button>

<div className={styles.FlowContainer}>
<div className={styles.AutomationName} data-testid="automationName">
{automationName ? (
Expand All @@ -262,7 +349,7 @@ export const FlowEditor = (props: FlowEditorProps) => {
<AutomationIcon />
</IconButton>

{automationName.flows[0].name}
{automationTitle}
</>
) : null}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/simulator/Simulator.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
position: fixed;
right: 0;
top: 100px;
z-index: 1000;
z-index: 10000;
}

.Simulator {
Expand Down
30 changes: 22 additions & 8 deletions src/components/simulator/Simulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,25 @@ import { SIMULATOR_CONTACT } from '../../common/constants';
export interface SimulatorProps {
showSimulator: boolean;
setShowSimulator: any;
simulatorIcon?: boolean;
message?: any;
}

export const Simulator: React.FC<SimulatorProps> = ({
showSimulator,
setShowSimulator,
simulatorIcon = true,
message = {},
}: SimulatorProps) => {
const [inputMessage, setInputMessage] = useState('');
const messageRef: any = useRef<HTMLDivElement>();

useEffect(() => {
if (message.keyword !== undefined) {
sendMessage();
}
}, [message.keyword]);

let messages = [];
let simulatorId = '';

Expand Down Expand Up @@ -93,6 +103,8 @@ export const Simulator: React.FC<SimulatorProps> = ({
.reverse();

const sendMessage = () => {
const sendMessage =
inputMessage === '' && message ? message.type + ':' + message.keyword : inputMessage;
axios({
method: 'POST',
url: GUPSHUP_CALLBACK_URL,
Expand All @@ -101,7 +113,7 @@ export const Simulator: React.FC<SimulatorProps> = ({
payload: {
type: 'text',
payload: {
text: inputMessage,
text: sendMessage,
},
sender: {
//this number will be the simulated contact number
Expand Down Expand Up @@ -134,8 +146,8 @@ export const Simulator: React.FC<SimulatorProps> = ({
<div className={styles.Screen}>
<div className={styles.Header}>
<ArrowBackIcon />
<img src={DefaultWhatsappImage} alt="default" />
<span>Beneficiary</span>
<img src={DefaultWhatsappImage} alt="default Image" />
<span data-testid="beneficiaryName">Beneficiary</span>
<div>
<VideocamIcon />
<CallIcon />
Expand Down Expand Up @@ -187,11 +199,13 @@ export const Simulator: React.FC<SimulatorProps> = ({
return (
<>
{showSimulator ? simulator : null}
<SimulatorIcon
data-testid="simulatorIcon"
className={showSimulator ? styles.SimulatorIconClicked : styles.SimulatorIconNormal}
onClick={() => handleSimulator()}
></SimulatorIcon>
{simulatorIcon ? (
<SimulatorIcon
data-testid="simulatorIcon"
className={showSimulator ? styles.SimulatorIconClicked : styles.SimulatorIconNormal}
onClick={() => handleSimulator()}
></SimulatorIcon>
) : null}
</>
);
};
3 changes: 2 additions & 1 deletion src/graphql/queries/Automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ export const FILTER_AUTOMATION = gql`
}
`;

export const GET_AUTOMATION_NAME = gql`
export const GET_AUTOMATION_DETAILS = gql`
query getFlowName($filter: FlowFilter!, $opts: Opts!) {
flows(filter: $filter, opts: $opts) {
name
keywords
}
}
`;
Loading