Skip to content

Commit

Permalink
Merge pull request #671 from glific/feature/automation-drafts
Browse files Browse the repository at this point in the history
Automation drafts feature added
  • Loading branch information
kurund authored Nov 5, 2020
2 parents 2ae24da + eb6ed60 commit 48a8a25
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 51 deletions.
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

0 comments on commit 48a8a25

Please sign in to comment.