up
This commit is contained in:
@@ -110,3 +110,105 @@
|
|||||||
.react-flow__panel {
|
.react-flow__panel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
foreignObject {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-node {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #007c89;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-flow {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-flow .menu-fixed {
|
||||||
|
background: #fff;
|
||||||
|
border: initial;
|
||||||
|
border-radius: initial;
|
||||||
|
border-right: 1px solid rgba(36, 28, 21, 0.15);
|
||||||
|
-webkit-transition: -webkit-transform 0.2s ease-in;
|
||||||
|
transition: -webkit-transform 0.2s ease-in;
|
||||||
|
transition: transform 0.2s ease-in;
|
||||||
|
transition: transform 0.2s ease-in, -webkit-transform 0.2s ease-in;
|
||||||
|
width: 360px;
|
||||||
|
z-index: 9;
|
||||||
|
padding: 20px;
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-flow .panelContainer {
|
||||||
|
width: calc(100% - 360px);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-flow .menu-fixed .item-flow {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.menu-fixed .item-flow b {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-fixed .list-option.flex {
|
||||||
|
grid-gap: 8px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-fixed .list-option .items {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid rgba(36, 28, 21, 0.15);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 16px 8px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: move;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-fixed .list-option.flex .step {
|
||||||
|
flex-direction: column;
|
||||||
|
height: 48px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-fixed .list-option.flex .items {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-fixed .list-option.flex .left {
|
||||||
|
border: 0;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
.menu-fixed .list-option.flex .right {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node.btn-add-node {
|
||||||
|
width: 40px !important;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
background: rgb(0, 124, 137);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node.btn-add-node i {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node.btn-add-node .react-flow__handle {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|||||||
7
src/assets/icons/icon-phone.svg
Normal file
7
src/assets/icons/icon-phone.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" focusable="false" aria-hidden="true" role="presentation"
|
||||||
|
class="wink-icon icon-ECm3x">
|
||||||
|
<path d="M13 19a1 1 0 11-2 0 1 1 0 012 0z"></path>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M6 1a2 2 0 00-2 2v18a2 2 0 002 2h12a2 2 0 002-2V3a2 2 0 00-2-2H6zm3 2H6v18h12V3h-3v1a1 1 0 01-1 1h-4a1 1 0 01-1-1V3z">
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 404 B |
@@ -27,13 +27,11 @@ body,
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
width: 600px;
|
width: 1334px;
|
||||||
height: 600px;
|
background: #fff;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup.hidden {
|
.popup.hidden {
|
||||||
@@ -41,12 +39,10 @@ body,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popup-content {
|
.popup-content {
|
||||||
background: #fff;
|
padding: 40px;
|
||||||
padding: 20px;
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: 100%;
|
overflow-y: auto;
|
||||||
max-width: 600px;
|
max-height: 600px;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-close {
|
.popup-close {
|
||||||
@@ -57,21 +53,24 @@ body,
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
.conent-form {
|
||||||
|
height: 450px;
|
||||||
|
border: 1px solid rgba(36, 28, 21, 0.15);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form label {
|
.conent-form label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
color: #333;
|
color: #333;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
form input,
|
.conent-form input,
|
||||||
form textarea,
|
.conent-form textarea,
|
||||||
form select {
|
.conent-form select {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
@@ -80,11 +79,11 @@ form select {
|
|||||||
width: 97%;
|
width: 97%;
|
||||||
}
|
}
|
||||||
|
|
||||||
form textarea {
|
.conent-form textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
form button {
|
.conent-form button {
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
background-color: #007bff;
|
background-color: #007bff;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -95,15 +94,15 @@ form button {
|
|||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
form button:hover {
|
.conent-form button:hover {
|
||||||
background-color: #0056b3;
|
background-color: #0056b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .form-group {
|
.conent-form .form-group {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .form-group:last-child {
|
.conent-form .form-group:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,3 +120,52 @@ form .form-group:last-child {
|
|||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
border-bottom: 1px solid rgba(36, 28, 21, 0.15);
|
||||||
|
padding: 10px 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header .close-popup {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 0;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-footer {
|
||||||
|
border-top: 1px solid rgba(36, 28, 21, 0.15);
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conent-form .content-left {
|
||||||
|
width: 50%;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conent-form .conent-right {
|
||||||
|
width: calc(100% - 50% - 30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-save {
|
||||||
|
margin-right: 16px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 50px;
|
||||||
|
outline: none;
|
||||||
|
background: #007c89;
|
||||||
|
color: #fff;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-remove {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 50px;
|
||||||
|
outline: none;
|
||||||
|
background: red;
|
||||||
|
color: #fff;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import App from './App';
|
|||||||
import NoteFlow from './nodes/NoteFlow';
|
import NoteFlow from './nodes/NoteFlow';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import NoteFlowNew from './nodes/NodeFlowNew';
|
import NoteFlowNew from './nodes/NodeFlowNew';
|
||||||
|
import FlowArea from './nodes/NodeMove';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|||||||
@@ -7,17 +7,55 @@ const CustomEdge = ({ id, sourceX, sourceY, targetX, targetY, data }) => {
|
|||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
targetX,
|
targetX,
|
||||||
targetY
|
targetY,
|
||||||
|
curvature: 0.5
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<defs>
|
||||||
|
<marker
|
||||||
|
id={`arrowhead-${id}`}
|
||||||
|
markerWidth='10'
|
||||||
|
markerHeight='7'
|
||||||
|
refX='4'
|
||||||
|
refY='3'
|
||||||
|
orient='auto'
|
||||||
|
markerUnits='strokeWidth'
|
||||||
|
>
|
||||||
|
<polygon points='0 0, 5 3.2, 0 7' fill='#222' />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
<path
|
<path
|
||||||
id={id}
|
id={id}
|
||||||
d={path}
|
d={path}
|
||||||
style={{ stroke: '#ccc', strokeWidth: .8, fill: 'transparent' }}
|
style={{ stroke: '#222', strokeWidth: 1.5, fill: 'transparent' }}
|
||||||
|
markerEnd={`url(#arrowhead-${id})`}
|
||||||
/>
|
/>
|
||||||
{data?.label && (
|
{data?.label1 && data?.label2 ? (
|
||||||
|
<>
|
||||||
|
<foreignObject
|
||||||
|
width={50}
|
||||||
|
height={8}
|
||||||
|
x={(sourceX + targetX) / 2 - 20}
|
||||||
|
y={sourceY - 2}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
xmlns='http://www.w3.org/1999/xhtml'
|
||||||
|
style={{
|
||||||
|
background: 'white',
|
||||||
|
padding: '3px 5px',
|
||||||
|
borderRadius: '0.8px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '13px',
|
||||||
|
border: '1px solid #ddd',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontWeight: '700'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{data.label1}
|
||||||
|
</div>
|
||||||
|
</foreignObject>
|
||||||
<foreignObject
|
<foreignObject
|
||||||
width={90}
|
width={90}
|
||||||
height={30}
|
height={30}
|
||||||
@@ -33,9 +71,33 @@ const CustomEdge = ({ id, sourceX, sourceY, targetX, targetY, data }) => {
|
|||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontSize: '13px'
|
fontSize: '13px'
|
||||||
}}
|
}}
|
||||||
onClick={data.onClick}
|
|
||||||
>
|
>
|
||||||
{data.label}
|
{data.label2}
|
||||||
|
</div>
|
||||||
|
</foreignObject>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<foreignObject
|
||||||
|
width={30}
|
||||||
|
height={30}
|
||||||
|
x={(sourceX + targetX) / 2 - 15}
|
||||||
|
y={(sourceY + targetY) / 2 - 15}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
xmlns='http://www.w3.org/1999/xhtml'
|
||||||
|
style={{
|
||||||
|
background: '#007c89',
|
||||||
|
width: '30px',
|
||||||
|
height: '30px',
|
||||||
|
lineHeight: '30px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '13px',
|
||||||
|
color: 'white',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{data?.label}
|
||||||
</div>
|
</div>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
)}
|
)}
|
||||||
|
|||||||
121
src/nodes/MenuFlow.tsx
Normal file
121
src/nodes/MenuFlow.tsx
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
// nội dung Menu
|
||||||
|
const MenuOptions = [
|
||||||
|
{
|
||||||
|
option: 'rule',
|
||||||
|
title: 'Rules',
|
||||||
|
list_option: [
|
||||||
|
{
|
||||||
|
key: 'time-delay',
|
||||||
|
label: 'Time delay',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: 'default',
|
||||||
|
icon: '/src/assets/icons/icon-time.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ifelse',
|
||||||
|
label: 'Replied to conversation?',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: 'default',
|
||||||
|
icon: '/src/assets/icons/icon-if.svg',
|
||||||
|
position: { x: 400, y: 290 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'wait',
|
||||||
|
label: 'Wait for trigger',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: 'default',
|
||||||
|
icon: '/src/assets/icons/icon-wait.svg'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
option: 'action',
|
||||||
|
title: 'Actions',
|
||||||
|
list_option: [
|
||||||
|
{
|
||||||
|
key: 'send-email',
|
||||||
|
label: 'Send email',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: '',
|
||||||
|
icon: '/src/assets/icons/icon-email.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'send-sms',
|
||||||
|
label: 'Send sms',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: '',
|
||||||
|
icon: '/src/assets/icons/icon-phone.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'send-survey-email',
|
||||||
|
label: 'Send survey in email',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: '',
|
||||||
|
icon: '/src/assets/icons/icon-list.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'contact-exists',
|
||||||
|
branchKey: 'yes',
|
||||||
|
label: 'Contact Exists',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: 'output',
|
||||||
|
icon: '/src/assets/icons/icon-check.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'send-survey',
|
||||||
|
branchKey: 'no',
|
||||||
|
label: 'Send survey',
|
||||||
|
id: nanoid(8),
|
||||||
|
type: 'output',
|
||||||
|
icon: '/src/assets/icons/icon-list.svg'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const MenuFlow = () => {
|
||||||
|
const onDragStart = (event, nodeType) => {
|
||||||
|
event.dataTransfer.setData(
|
||||||
|
'application/reactflow',
|
||||||
|
JSON.stringify(nodeType)
|
||||||
|
);
|
||||||
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='menu-fixed'>
|
||||||
|
<b></b>
|
||||||
|
{MenuOptions.map((option) => (
|
||||||
|
<div className='item-flow' key={option.option}>
|
||||||
|
<b>{option.title}</b>
|
||||||
|
<div
|
||||||
|
className={`list-option ${option.option == 'rule' ? 'flex' : ''}`}
|
||||||
|
>
|
||||||
|
{option.list_option.map((items) => (
|
||||||
|
<div
|
||||||
|
className='items'
|
||||||
|
draggable='true'
|
||||||
|
key={items.id}
|
||||||
|
onDragStart={(event) => onDragStart(event, items)}
|
||||||
|
>
|
||||||
|
<div className='step'>
|
||||||
|
<div className='left'>
|
||||||
|
<img src={items.icon} width='24px' height='24px' alt='' />
|
||||||
|
</div>
|
||||||
|
<div className='right'>{items.label}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MenuFlow;
|
||||||
@@ -5,16 +5,18 @@ import {
|
|||||||
Controls,
|
Controls,
|
||||||
Background,
|
Background,
|
||||||
BackgroundVariant,
|
BackgroundVariant,
|
||||||
SelectionMode
|
SelectionMode,
|
||||||
|
applyNodeChanges,
|
||||||
|
applyEdgeChanges,
|
||||||
|
MarkerType
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
import { FiUser, FiMail, FiCheckCircle, FiList } from 'react-icons/fi';
|
|
||||||
import { FaShuffle } from 'react-icons/fa6';
|
|
||||||
import '../assets/css/style.css';
|
import '../assets/css/style.css';
|
||||||
import Tabbar from './Tabbar.tsx';
|
import Tabbar from './Tabbar.tsx';
|
||||||
import ShowPopup from './ShowPopup.tsx';
|
import ShowPopup from './ShowPopup.tsx';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import CustomEdge from './CustomEdge';
|
import CustomEdge from './CustomEdge';
|
||||||
|
import MenuFlow from './MenuFlow';
|
||||||
|
|
||||||
const initialNodes = [
|
const initialNodes = [
|
||||||
{
|
{
|
||||||
@@ -26,7 +28,7 @@ const initialNodes = [
|
|||||||
<div className='step'>
|
<div className='step'>
|
||||||
<div className='left'>
|
<div className='left'>
|
||||||
<img
|
<img
|
||||||
src='/src/assets/icons/icon-user.svg'
|
src='/src/assets/icons/icon-time.svg'
|
||||||
width='24px'
|
width='24px'
|
||||||
height='24px'
|
height='24px'
|
||||||
alt=''
|
alt=''
|
||||||
@@ -40,8 +42,16 @@ const initialNodes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
data: { label: 'Add Note' },
|
key: 'add-node',
|
||||||
position: { x: 250, y: 250 }
|
type: 'output',
|
||||||
|
data: {
|
||||||
|
label: (
|
||||||
|
<div className='add-node'>
|
||||||
|
<i>+</i> <span>Add a journey point</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
position: { x: 250, y: 220 }
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -78,11 +88,17 @@ const nodeContents = {
|
|||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const connectEdges = [
|
||||||
|
{
|
||||||
|
id: 'e1-2',
|
||||||
|
source: '1',
|
||||||
|
target: '2'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const NoteFlow = () => {
|
const NoteFlow = () => {
|
||||||
const [nodes, setNodes] = useState(initialNodes);
|
const [nodes, setNodes] = useState(initialNodes);
|
||||||
const [edges, setEdges] = useState([
|
const [edges, setEdges] = useState(connectEdges);
|
||||||
{ id: 'e1-2', source: '1', target: '2' } // Kết nối node "Add Note" với node mặc định ban đầu
|
|
||||||
]);
|
|
||||||
const [showTabBar, setShowTabBar] = useState(false);
|
const [showTabBar, setShowTabBar] = useState(false);
|
||||||
const [lastNodeId, setLastNodeId] = useState('1');
|
const [lastNodeId, setLastNodeId] = useState('1');
|
||||||
const [nodeToDelete, setNodeToDelete] = useState(null);
|
const [nodeToDelete, setNodeToDelete] = useState(null);
|
||||||
@@ -99,13 +115,14 @@ const NoteFlow = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const closeTabbar = () => {
|
const closeTabbar = () => {
|
||||||
console.log('aaa');
|
|
||||||
setIsTabbarVisible(false); // Ẩn tabbar khi click close
|
setIsTabbarVisible(false); // Ẩn tabbar khi click close
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNodeClick = (event, node) => {
|
const onNodeClick = (event, node) => {
|
||||||
if (node.id === '2') {
|
console.log(node);
|
||||||
// Check if the clicked node is "Add Note"
|
if (node.key === 'add-node') {
|
||||||
|
console.log(node);
|
||||||
|
// Check if the clicked node is "Add Node"
|
||||||
setShowTabBar(true);
|
setShowTabBar(true);
|
||||||
setIsTabbarVisible(true);
|
setIsTabbarVisible(true);
|
||||||
} else {
|
} else {
|
||||||
@@ -165,14 +182,13 @@ const NoteFlow = () => {
|
|||||||
|
|
||||||
const addNode = (option) => {
|
const addNode = (option) => {
|
||||||
const newNodeId = nanoid(8); // Generate a unique ID for the new node
|
const newNodeId = nanoid(8); // Generate a unique ID for the new node
|
||||||
const addNoteNode = nodes.find((node) => node.id === '2');
|
const addNoteNode = nodes.find((node) => node.key === 'add-node');
|
||||||
const initialX = addNoteNode.position.x;
|
const initialX = addNoteNode?.position.x;
|
||||||
console.log(initialX);
|
|
||||||
|
|
||||||
const nextY =
|
const nextY =
|
||||||
nodes.length > 2
|
nodes.length > 2
|
||||||
? getNextYPosition(initialX, 130)
|
? getNextYPosition(initialX, 120)
|
||||||
: addNoteNode.position.y;
|
: addNoteNode?.position.y;
|
||||||
|
|
||||||
// Remove existing edges from addNoteNode
|
// Remove existing edges from addNoteNode
|
||||||
setEdges((eds) =>
|
setEdges((eds) =>
|
||||||
@@ -270,14 +286,12 @@ const NoteFlow = () => {
|
|||||||
|
|
||||||
setLastNodeId(noNode.id);
|
setLastNodeId(noNode.id);
|
||||||
} else {
|
} else {
|
||||||
console.log(option);
|
|
||||||
|
|
||||||
// Add new node logic
|
// Add new node logic
|
||||||
if (addNoteNode) {
|
if (addNoteNode) {
|
||||||
const initialX = addNoteNode.position.x;
|
const initialX = addNoteNode.position.x;
|
||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
nds.map((node) =>
|
nds.map((node) =>
|
||||||
node.id === '2'
|
node.key === 'add-node'
|
||||||
? {
|
? {
|
||||||
...node,
|
...node,
|
||||||
position: {
|
position: {
|
||||||
@@ -303,6 +317,10 @@ const NoteFlow = () => {
|
|||||||
id: nanoid(8),
|
id: nanoid(8),
|
||||||
source: lastNodeId,
|
source: lastNodeId,
|
||||||
target: newNodeId
|
target: newNodeId
|
||||||
|
// data: {
|
||||||
|
// label: <div onClick={handleAddNoteClick}>+</div>,
|
||||||
|
// onClick: handleAddNoteClick
|
||||||
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
setNodes((nds) => [...nds, newNode]);
|
setNodes((nds) => [...nds, newNode]);
|
||||||
@@ -315,15 +333,17 @@ const NoteFlow = () => {
|
|||||||
// setEdges((eds) => [
|
// setEdges((eds) => [
|
||||||
// ...eds,
|
// ...eds,
|
||||||
// {
|
// {
|
||||||
// id: uuidv4(),
|
// id: nanoid(8),
|
||||||
// source: lastNodeId,
|
// source: lastNodeId,
|
||||||
// data: {
|
// data: {
|
||||||
// label: <div onClick={handleAddNoteClick}>Add Note</div>, // Use HTML or React component
|
// label: <div onClick={handleAddNoteClick}>Add Note</div>, // Use HTML or React component
|
||||||
// onClick: handleAddNoteClick
|
// onClick: handleAddNoteClick
|
||||||
// },
|
// },
|
||||||
// target: newNodeId
|
// target: newNodeId
|
||||||
// }
|
// },
|
||||||
|
// { id: nanoid(8), source: newNodeId, target: addNoteNode.id }
|
||||||
// ]);
|
// ]);
|
||||||
|
|
||||||
// Cuộn tới node mới thêm
|
// Cuộn tới node mới thêm
|
||||||
setLastNodeId(newNodeId);
|
setLastNodeId(newNodeId);
|
||||||
}
|
}
|
||||||
@@ -340,21 +360,361 @@ const NoteFlow = () => {
|
|||||||
setSelectedNodeContent(content); // Cập nhật nội dung được chọn
|
setSelectedNodeContent(content); // Cập nhật nội dung được chọn
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onDrop = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const type = JSON.parse(
|
||||||
|
event.dataTransfer.getData('application/reactflow')
|
||||||
|
);
|
||||||
|
const reactFlowBounds = event.target.getBoundingClientRect();
|
||||||
|
const position = {
|
||||||
|
x: event.clientX - reactFlowBounds.left - 100,
|
||||||
|
y: event.clientY - reactFlowBounds.top + 5
|
||||||
|
};
|
||||||
|
|
||||||
|
const newNodeId = nanoid(8);
|
||||||
|
// Xử lý add-node với id '2'
|
||||||
|
const addNodeId = '2';
|
||||||
|
const addNode = nodes.find((node) => node.id === addNodeId);
|
||||||
|
|
||||||
|
// Tìm các edge nối với node có id '2'
|
||||||
|
const targetEdge = edges.find(
|
||||||
|
(edge) => edge?.source === '2' || edge?.target === '2'
|
||||||
|
);
|
||||||
|
const targetNodeId =
|
||||||
|
targetEdge?.source === '2' ? targetEdge?.target : targetEdge?.source;
|
||||||
|
|
||||||
|
const targetNode = nodes.find((node) => node.id === targetNodeId);
|
||||||
|
|
||||||
|
// Cập nhật vị trí của node mới để cách node đang kết nối 200 đơn vị
|
||||||
|
const newNodePosition = {
|
||||||
|
x: targetNode?.position.x,
|
||||||
|
y: targetNode?.position.y + 110
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tạo node mới
|
||||||
|
const newNode = {
|
||||||
|
id: newNodeId,
|
||||||
|
key: type.key,
|
||||||
|
type: type.type || 'default',
|
||||||
|
data: {
|
||||||
|
label: (
|
||||||
|
<div className='step'>
|
||||||
|
<div className='left'>
|
||||||
|
<img src={type.icon} width='24px' height='24px' alt='' />
|
||||||
|
</div>
|
||||||
|
<div className='right'>{type.label}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
position: newNodePosition
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loại bỏ các edge liên quan đến node '2'
|
||||||
|
setEdges((eds) =>
|
||||||
|
eds.filter((edge) => edge.source !== '2' && edge.target !== '2')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Kiểm tra điều kiện node là 'ifelse'
|
||||||
|
if (type.key === 'ifelse') {
|
||||||
|
setNodes((nds) => nds.filter((node) => node.id !== addNodeId));
|
||||||
|
const yesNodeId = nanoid(8);
|
||||||
|
const noNodeId = nanoid(8);
|
||||||
|
|
||||||
|
// Tạo node Yes
|
||||||
|
const yesNode = {
|
||||||
|
id: yesNodeId,
|
||||||
|
key: 'add-node',
|
||||||
|
brand: 'yes',
|
||||||
|
data: {
|
||||||
|
label: (
|
||||||
|
<div className='add-node'>
|
||||||
|
<i>+</i> <span>Add a journey point</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
x: newNode.position.x - 200, // Điều chỉnh vị trí của nhánh Yes
|
||||||
|
y: newNode.position.y + 150 // Điều chỉnh vị trí của nhánh Yes
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tạo node No
|
||||||
|
const noNode = {
|
||||||
|
id: noNodeId,
|
||||||
|
key: 'add-node',
|
||||||
|
brand: 'no',
|
||||||
|
data: {
|
||||||
|
label: (
|
||||||
|
<div className='add-node'>
|
||||||
|
<i>+</i> <span>Add a journey point</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
x: newNode?.position.x + 200, // Điều chỉnh vị trí của nhánh No
|
||||||
|
y: newNode?.position.y + 150 // Điều chỉnh vị trí của nhánh No
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tạo các edge nối từ node ifelse đến Yes và No
|
||||||
|
const ifelseEdges = [
|
||||||
|
{
|
||||||
|
id: nanoid(8),
|
||||||
|
source: targetNodeId,
|
||||||
|
target: newNodeId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: nanoid(8),
|
||||||
|
source: newNodeId,
|
||||||
|
target: yesNodeId,
|
||||||
|
type: 'custom',
|
||||||
|
data: {
|
||||||
|
label: 'Yes'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: nanoid(8),
|
||||||
|
source: newNodeId,
|
||||||
|
target: noNodeId,
|
||||||
|
type: 'custom',
|
||||||
|
data: {
|
||||||
|
label: 'No'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Thêm node yes, no và edge vào danh sách node và edges
|
||||||
|
setNodes((nds) => [...nds, newNode, yesNode, noNode]);
|
||||||
|
setEdges((eds) => [...eds, ...ifelseEdges]);
|
||||||
|
} else {
|
||||||
|
// Nếu không phải 'ifelse', kiểm tra các điều kiện của node bình thường
|
||||||
|
|
||||||
|
if (targetNode) {
|
||||||
|
setNodes((nds) => [...nds, newNode]);
|
||||||
|
setEdges((eds) => [
|
||||||
|
...eds,
|
||||||
|
{
|
||||||
|
id: nanoid(8),
|
||||||
|
source: targetNodeId,
|
||||||
|
target: newNodeId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: nanoid(8),
|
||||||
|
source: newNodeId,
|
||||||
|
target: '2'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (addNode) {
|
||||||
|
const addNodePosition = {
|
||||||
|
x: addNode.position.x,
|
||||||
|
y: addNode.position.y + 110
|
||||||
|
};
|
||||||
|
|
||||||
|
setNodes((nds) =>
|
||||||
|
nds.map((node) =>
|
||||||
|
node.id === addNodeId
|
||||||
|
? { ...node, position: addNodePosition }
|
||||||
|
: node
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
setEdges((eds) =>
|
||||||
|
eds.filter(
|
||||||
|
(edge) => edge.source !== addNodeId && edge.target !== addNodeId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Thêm edge mới nối add-node và node mới
|
||||||
|
setEdges((eds) => [
|
||||||
|
...eds,
|
||||||
|
{
|
||||||
|
id: nanoid(8),
|
||||||
|
source: newNodeId,
|
||||||
|
target: addNodeId
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Kiểm tra nếu node mới là 'contact-exists', ẩn add-node
|
||||||
|
if (type.key === 'contact-exists') {
|
||||||
|
setNodes((nds) => nds.filter((node) => node.id !== addNodeId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const newNodeNew = {
|
||||||
|
id: newNodeId,
|
||||||
|
key: type.key,
|
||||||
|
type: type.type || 'default',
|
||||||
|
data: {
|
||||||
|
label: (
|
||||||
|
<div className='step'>
|
||||||
|
<div className='left'>
|
||||||
|
<img src={type.icon} width='24px' height='24px' alt='' />
|
||||||
|
</div>
|
||||||
|
<div className='right'>{type.label}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
position: position
|
||||||
|
};
|
||||||
|
|
||||||
|
setNodes((nds) => [...nds, newNodeNew]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragOver = useCallback((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.dataTransfer.dropEffect = 'move';
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onNodesChange = useCallback(
|
||||||
|
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
|
||||||
|
[setNodes]
|
||||||
|
);
|
||||||
|
const onEdgesChange = useCallback(
|
||||||
|
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
||||||
|
[setEdges]
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultEdgeOptions = {
|
||||||
|
style: { strokeWidth: 1.5, stroke: 'black' },
|
||||||
|
type: 'floating',
|
||||||
|
markerEnd: {
|
||||||
|
type: MarkerType.ArrowClosed,
|
||||||
|
color: 'black'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConnect = useCallback(
|
||||||
|
(params) => setEdges((eds) => addEdge(params, eds)),
|
||||||
|
[setEdges]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Hàm tính khoảng cách giữa hai node
|
||||||
|
const getDistance = (pos1, pos2) => {
|
||||||
|
return Math.sqrt(
|
||||||
|
Math.pow(pos1.x - pos2.x, 2) + Math.pow(pos1.y - pos2.y, 2)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hàm xử lý khi dừng kéo node
|
||||||
|
const onNodeDragStop = useCallback(
|
||||||
|
(event, node) => {
|
||||||
|
const threshold = 50; // khoảng cách cho phép để node bị "đè"
|
||||||
|
let closestNode = null;
|
||||||
|
let closestDistance = Infinity;
|
||||||
|
|
||||||
|
nodes.forEach((item) => {
|
||||||
|
if (item.id !== node.id) {
|
||||||
|
const distance = getDistance(node.position, item.position);
|
||||||
|
if (distance < closestDistance && distance <= threshold) {
|
||||||
|
closestNode = item;
|
||||||
|
closestDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (closestNode) {
|
||||||
|
// Thay thế node cũ bằng node mới
|
||||||
|
const updatedEdges = edges.map((edge) => {
|
||||||
|
if (edge.source === closestNode.id) {
|
||||||
|
return { ...edge, source: node.id }; // Nối node mới làm source
|
||||||
|
}
|
||||||
|
if (edge.target === closestNode.id) {
|
||||||
|
return { ...edge, target: node.id }; // Nối node mới làm target
|
||||||
|
}
|
||||||
|
return edge;
|
||||||
|
});
|
||||||
|
if (node.type != 'output') {
|
||||||
|
// Tạo một node mới với nội dung "Add a journey point" ngay dưới node thay thế
|
||||||
|
const newNode = {
|
||||||
|
id: nanoid(8),
|
||||||
|
key: 'add-node',
|
||||||
|
brand: node.brand,
|
||||||
|
position: {
|
||||||
|
x: node.position.x, // Giữ nguyên vị trí x
|
||||||
|
y: node.position.y + 100 // Đặt node mới xuống dưới 150 đơn vị y
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
label: (
|
||||||
|
<div className='add-node'>
|
||||||
|
<i>+</i> <span>Add a journey point</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Thay thế node cũ bằng node mới
|
||||||
|
setNodes((nds) => [
|
||||||
|
...nds.map((n) =>
|
||||||
|
n.id === closestNode.id
|
||||||
|
? { ...closestNode, ...node, position: node.position }
|
||||||
|
: n
|
||||||
|
),
|
||||||
|
newNode // Thêm node mới
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Cập nhật edges
|
||||||
|
setEdges([
|
||||||
|
...updatedEdges,
|
||||||
|
{
|
||||||
|
id: nanoid(8), // ID của edge mới
|
||||||
|
source: node.id, // Nối từ node vừa thay thế
|
||||||
|
target: newNode.id // Đến node "Add a journey point"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// Thay thế node cũ bằng node mới
|
||||||
|
setNodes((nds) => [
|
||||||
|
...nds.map((n) =>
|
||||||
|
n.id === closestNode.id
|
||||||
|
? { ...closestNode, ...node, position: node.position }
|
||||||
|
: n
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Cập nhật edges
|
||||||
|
setEdges(updatedEdges);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Không có node gần, cập nhật node vào vị trí mới
|
||||||
|
setNodes((nds) =>
|
||||||
|
nds.map((n) =>
|
||||||
|
n.id === node.id ? { ...n, position: node.position } : n
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[nodes, edges, setNodes, setEdges]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: '100vh', width: '100%', position: 'relative' }}>
|
<div style={{ overflowY: 'auto', position: 'relative' }}>
|
||||||
|
<div className='flex-flow'>
|
||||||
|
<MenuFlow />
|
||||||
|
<div className='panelContainer'>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
edgeTypes={{ custom: CustomEdge }}
|
edgeTypes={{ custom: CustomEdge }}
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
onNodeContextMenu={onNodeContextMenu}
|
onNodeContextMenu={onNodeContextMenu}
|
||||||
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
|
onDrop={onDrop}
|
||||||
|
onDragOver={onDragOver}
|
||||||
panOnScroll
|
panOnScroll
|
||||||
selectionOnDrag
|
defaultEdgeOptions={defaultEdgeOptions}
|
||||||
selectionMode={SelectionMode.Partial}
|
onConnect={onConnect}
|
||||||
|
onNodeDragStop={onNodeDragStop}
|
||||||
>
|
>
|
||||||
<Controls />
|
<Controls position='right-bottom' />
|
||||||
<Background variant={BackgroundVariant.Lines} color='#ccccc' />
|
<Background variant={BackgroundVariant.Lines} color='#ccccc' />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{isTabbarVisible && (
|
{isTabbarVisible && (
|
||||||
<Tabbar addNode={addNode} closeTabbar={closeTabbar} />
|
<Tabbar addNode={addNode} closeTabbar={closeTabbar} />
|
||||||
|
|||||||
@@ -18,14 +18,27 @@ const ShowPopup = ({ selectedNodeContent, hidePopup }) => {
|
|||||||
<>
|
<>
|
||||||
<div className='popup-overlay' onClick={handleOverlayClick}></div>
|
<div className='popup-overlay' onClick={handleOverlayClick}></div>
|
||||||
<div className={`popup global-popup ${showPopup ? '' : 'hidden'}`}>
|
<div className={`popup global-popup ${showPopup ? '' : 'hidden'}`}>
|
||||||
|
<div className='popup-header'>
|
||||||
|
<h1 className='title'></h1>
|
||||||
|
<div className='close-popup' onClick={handleOverlayClick}>
|
||||||
|
x
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className='popup-content'>
|
<div className='popup-content'>
|
||||||
<div className='conent-form'>
|
<div className='conent-form'>
|
||||||
<form>
|
<div
|
||||||
<div dangerouslySetInnerHTML={{ __html: selectedNodeContent }} />
|
className='content-left'
|
||||||
<button onClick={() => setShowPopup(false)}>Close</button>
|
dangerouslySetInnerHTML={{ __html: selectedNodeContent }}
|
||||||
</form>
|
/>
|
||||||
|
<div className='content-right'></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='popup-footer '>
|
||||||
|
<button onClick={() => setShowPopup(false)} className='btn-save'>
|
||||||
|
Lưu lại
|
||||||
|
</button>
|
||||||
|
<button className='btn-remove'>Xóa</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const tabBarOptions = [
|
|||||||
<div className='right'>Send email</div>
|
<div className='right'>Send email</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
position: { x: 250, y: 0 }
|
position: { x: 400, y: 0 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ifelse',
|
key: 'ifelse',
|
||||||
@@ -91,10 +91,11 @@ const tabBarOptions = [
|
|||||||
<div className='right'>Replied to conversation?</div>
|
<div className='right'>Replied to conversation?</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
position: { x: 250, y: 290 }
|
position: { x: 400, y: 290 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'send-survey',
|
key: 'send-survey',
|
||||||
|
branchKey: 'no',
|
||||||
label: 'Send survey',
|
label: 'Send survey',
|
||||||
id: '5',
|
id: '5',
|
||||||
type: 'output',
|
type: 'output',
|
||||||
@@ -122,10 +123,11 @@ const tabBarOptions = [
|
|||||||
<div className='right'>Send survey</div>
|
<div className='right'>Send survey</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
position: { x: 250, y: 400 }
|
position: { x: 400, y: 400 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'contact-exists',
|
key: 'contact-exists',
|
||||||
|
branchKey: 'yes',
|
||||||
label: 'Contact Exists',
|
label: 'Contact Exists',
|
||||||
id: '6',
|
id: '6',
|
||||||
type: 'output',
|
type: 'output',
|
||||||
@@ -153,7 +155,7 @@ const tabBarOptions = [
|
|||||||
<div className='right'>Contact Exists</div>
|
<div className='right'>Contact Exists</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
position: { x: 250, y: 400 }
|
position: { x: 400, y: 400 }
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -175,7 +177,7 @@ const Tabbar = ({ addNode, closeTabbar }) => {
|
|||||||
✖
|
✖
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className='tittle'>Rules</h3>
|
<h3 className='tittle'>Action</h3>
|
||||||
<div className='itemTab grid'>
|
<div className='itemTab grid'>
|
||||||
{tabBarOptions.map((option) => (
|
{tabBarOptions.map((option) => (
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user