This commit is contained in:
2024-09-18 22:46:14 +07:00
parent f1f47c9e30
commit 6700e9514e
9 changed files with 793 additions and 77 deletions

View File

@@ -110,3 +110,105 @@
.react-flow__panel {
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;
}

View 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

View File

@@ -27,13 +27,11 @@ body,
position: fixed;
top: 50%;
left: 50%;
width: 600px;
height: 600px;
display: flex;
align-items: center;
justify-content: center;
width: 1334px;
background: #fff;
z-index: 1000;
transform: translate(-50%, -50%);
border-radius: 12px;
}
.popup.hidden {
@@ -41,12 +39,10 @@ body,
}
.popup-content {
background: #fff;
padding: 20px;
padding: 40px;
border-radius: 5px;
width: 100%;
max-width: 600px;
position: relative;
overflow-y: auto;
max-height: 600px;
}
.popup-close {
@@ -57,21 +53,24 @@ body,
cursor: pointer;
}
form {
.conent-form {
height: 450px;
border: 1px solid rgba(36, 28, 21, 0.15);
padding: 20px;
border-radius: 10px;
display: flex;
flex-direction: column;
}
form label {
.conent-form label {
font-weight: bold;
margin-bottom: 5px;
color: #333;
display: block;
}
form input,
form textarea,
form select {
.conent-form input,
.conent-form textarea,
.conent-form select {
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ddd;
@@ -80,11 +79,11 @@ form select {
width: 97%;
}
form textarea {
.conent-form textarea {
resize: vertical;
}
form button {
.conent-form button {
padding: 10px 15px;
background-color: #007bff;
border: none;
@@ -95,15 +94,15 @@ form button {
transition: background-color 0.3s;
}
form button:hover {
.conent-form button:hover {
background-color: #0056b3;
}
form .form-group {
.conent-form .form-group {
margin-bottom: 15px;
}
form .form-group:last-child {
.conent-form .form-group:last-child {
margin-bottom: 0;
}
@@ -121,3 +120,52 @@ form .form-group:last-child {
background: rgba(0, 0, 0, 0.5);
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;
}

View File

@@ -6,6 +6,7 @@ import App from './App';
import NoteFlow from './nodes/NoteFlow';
import './index.css';
import NoteFlowNew from './nodes/NodeFlowNew';
import FlowArea from './nodes/NodeMove';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>

View File

@@ -7,17 +7,55 @@ const CustomEdge = ({ id, sourceX, sourceY, targetX, targetY, data }) => {
sourceX,
sourceY,
targetX,
targetY
targetY,
curvature: 0.5
});
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
id={id}
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
width={90}
height={30}
@@ -33,9 +71,33 @@ const CustomEdge = ({ id, sourceX, sourceY, targetX, targetY, data }) => {
cursor: 'pointer',
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>
</foreignObject>
)}

121
src/nodes/MenuFlow.tsx Normal file
View 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;

View File

@@ -5,16 +5,18 @@ import {
Controls,
Background,
BackgroundVariant,
SelectionMode
SelectionMode,
applyNodeChanges,
applyEdgeChanges,
MarkerType
} from '@xyflow/react';
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 Tabbar from './Tabbar.tsx';
import ShowPopup from './ShowPopup.tsx';
import { nanoid } from 'nanoid';
import CustomEdge from './CustomEdge';
import MenuFlow from './MenuFlow';
const initialNodes = [
{
@@ -26,7 +28,7 @@ const initialNodes = [
<div className='step'>
<div className='left'>
<img
src='/src/assets/icons/icon-user.svg'
src='/src/assets/icons/icon-time.svg'
width='24px'
height='24px'
alt=''
@@ -40,8 +42,16 @@ const initialNodes = [
},
{
id: '2',
data: { label: 'Add Note' },
position: { x: 250, y: 250 }
key: 'add-node',
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 [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState([
{ id: 'e1-2', source: '1', target: '2' } // Kết nối node "Add Note" với node mặc định ban đầu
]);
const [edges, setEdges] = useState(connectEdges);
const [showTabBar, setShowTabBar] = useState(false);
const [lastNodeId, setLastNodeId] = useState('1');
const [nodeToDelete, setNodeToDelete] = useState(null);
@@ -99,13 +115,14 @@ const NoteFlow = () => {
});
};
const closeTabbar = () => {
console.log('aaa');
setIsTabbarVisible(false); // Ẩn tabbar khi click close
};
const onNodeClick = (event, node) => {
if (node.id === '2') {
// Check if the clicked node is "Add Note"
console.log(node);
if (node.key === 'add-node') {
console.log(node);
// Check if the clicked node is "Add Node"
setShowTabBar(true);
setIsTabbarVisible(true);
} else {
@@ -165,14 +182,13 @@ const NoteFlow = () => {
const addNode = (option) => {
const newNodeId = nanoid(8); // Generate a unique ID for the new node
const addNoteNode = nodes.find((node) => node.id === '2');
const initialX = addNoteNode.position.x;
console.log(initialX);
const addNoteNode = nodes.find((node) => node.key === 'add-node');
const initialX = addNoteNode?.position.x;
const nextY =
nodes.length > 2
? getNextYPosition(initialX, 130)
: addNoteNode.position.y;
? getNextYPosition(initialX, 120)
: addNoteNode?.position.y;
// Remove existing edges from addNoteNode
setEdges((eds) =>
@@ -270,14 +286,12 @@ const NoteFlow = () => {
setLastNodeId(noNode.id);
} else {
console.log(option);
// Add new node logic
if (addNoteNode) {
const initialX = addNoteNode.position.x;
setNodes((nds) =>
nds.map((node) =>
node.id === '2'
node.key === 'add-node'
? {
...node,
position: {
@@ -303,6 +317,10 @@ const NoteFlow = () => {
id: nanoid(8),
source: lastNodeId,
target: newNodeId
// data: {
// label: <div onClick={handleAddNoteClick}>+</div>,
// onClick: handleAddNoteClick
// }
};
setNodes((nds) => [...nds, newNode]);
@@ -315,15 +333,17 @@ const NoteFlow = () => {
// setEdges((eds) => [
// ...eds,
// {
// id: uuidv4(),
// id: nanoid(8),
// source: lastNodeId,
// data: {
// label: <div onClick={handleAddNoteClick}>Add Note</div>, // Use HTML or React component
// onClick: handleAddNoteClick
// },
// target: newNodeId
// }
// },
// { id: nanoid(8), source: newNodeId, target: addNoteNode.id }
// ]);
// Cuộn tới node mới thêm
setLastNodeId(newNodeId);
}
@@ -340,21 +360,361 @@ const NoteFlow = () => {
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 (
<div style={{ height: '100vh', width: '100%', position: 'relative' }}>
<div style={{ overflowY: 'auto', position: 'relative' }}>
<div className='flex-flow'>
<MenuFlow />
<div className='panelContainer'>
<ReactFlow
nodes={nodes}
edges={edges}
edgeTypes={{ custom: CustomEdge }}
onNodeClick={onNodeClick}
onNodeContextMenu={onNodeContextMenu}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onDrop={onDrop}
onDragOver={onDragOver}
panOnScroll
selectionOnDrag
selectionMode={SelectionMode.Partial}
defaultEdgeOptions={defaultEdgeOptions}
onConnect={onConnect}
onNodeDragStop={onNodeDragStop}
>
<Controls />
<Controls position='right-bottom' />
<Background variant={BackgroundVariant.Lines} color='#ccccc' />
</ReactFlow>
</div>
</div>
{isTabbarVisible && (
<Tabbar addNode={addNode} closeTabbar={closeTabbar} />

View File

@@ -18,14 +18,27 @@ const ShowPopup = ({ selectedNodeContent, hidePopup }) => {
<>
<div className='popup-overlay' onClick={handleOverlayClick}></div>
<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='conent-form'>
<form>
<div dangerouslySetInnerHTML={{ __html: selectedNodeContent }} />
<button onClick={() => setShowPopup(false)}>Close</button>
</form>
<div
className='content-left'
dangerouslySetInnerHTML={{ __html: selectedNodeContent }}
/>
<div className='content-right'></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>
</>
);

View File

@@ -60,7 +60,7 @@ const tabBarOptions = [
<div className='right'>Send email</div>
</div>
),
position: { x: 250, y: 0 }
position: { x: 400, y: 0 }
},
{
key: 'ifelse',
@@ -91,10 +91,11 @@ const tabBarOptions = [
<div className='right'>Replied to conversation?</div>
</div>
),
position: { x: 250, y: 290 }
position: { x: 400, y: 290 }
},
{
key: 'send-survey',
branchKey: 'no',
label: 'Send survey',
id: '5',
type: 'output',
@@ -122,10 +123,11 @@ const tabBarOptions = [
<div className='right'>Send survey</div>
</div>
),
position: { x: 250, y: 400 }
position: { x: 400, y: 400 }
},
{
key: 'contact-exists',
branchKey: 'yes',
label: 'Contact Exists',
id: '6',
type: 'output',
@@ -153,7 +155,7 @@ const tabBarOptions = [
<div className='right'>Contact Exists</div>
</div>
),
position: { x: 250, y: 400 }
position: { x: 400, y: 400 }
}
];
@@ -175,7 +177,7 @@ const Tabbar = ({ addNode, closeTabbar }) => {
</div>
</div>
<h3 className='tittle'>Rules</h3>
<h3 className='tittle'>Action</h3>
<div className='itemTab grid'>
{tabBarOptions.map((option) => (
<div