Message
Message
useState,
useEffect,
useReducer,
useContext,
useCallback
} from "react";
import "reactflow/dist/style.css";
import ReactFlow, {
MiniMap,
Controls,
Background,
useNodesState,
useEdgesState,
addEdge,
onElementsRemove,
useReactFlow
} from "react-flow-renderer";
import FlowBuilderAddTextModal from "../../components/FlowBuilderAddTextModal";
import FlowBuilderIntervalModal from "../../components/FlowBuilderIntervalModal";
import startNode from "./nodes/startNode";
import conditionNode from "./nodes/conditionNode";
import menuNode from "./nodes/menuNode";
import intervalNode from "./nodes/intervalNode";
import imgNode from "./nodes/imgNode";
import randomizerNode from "./nodes/randomizerNode";
import videoNode from "./nodes/videoNode";
import FlowBuilderConditionModal from "../../components/FlowBuilderConditionModal";
import AccountTreeIcon from "@mui/icons-material/AccountTree";
import FlowBuilderManipulatorModal from
"../../components/FlowBuilderManipulatorModal";
import FlowBuilderMenuModal from "../../components/FlowBuilderMenuModal";
import {
AccessTime,
CallSplit,
DynamicFeed,
Image,
ImportExport,
LibraryBooks,
Message,
MicNone,
RocketLaunch,
Rule,
Videocam,
} from "@mui/icons-material";
import RemoveEdge from "./nodes/removeEdge";
import FlowBuilderAddImgModal from "../../components/FlowBuilderAddImgModal";
import FlowBuilderTicketModal from "../../components/FlowBuilderAddTicketModal";
import FlowBuilderAddFlowConection from
"../../components/FlowBuilderAddFlowConection/index.js";
import FlowBuilderAddAudioModal from "../../components/FlowBuilderAddAudioModal";
import audioNode from "./nodes/audioNode";
import { useNodeStorage } from "../../stores/useNodeStorage";
import FlowBuilderRandomizerModal from
"../../components/FlowBuilderRandomizerModal";
import FlowBuilderAddVideoModal from "../../components/FlowBuilderAddVideoModal";
import FlowBuilderSingleBlockModal from
"../../components/FlowBuilderSingleBlockModal";
import singleBlockNode from "./nodes/singleBlockNode";
import { colorPrimary } from "../../styles/styles";
import ticketNode from "./nodes/ticketNode";
import chatControllerNode from "./nodes/chatControllerNode.js";
import { ConfirmationNumber } from "@material-ui/icons";
import FlowBuilderChatControllerModal from
"../../components/FlowBuilderChatControllerModal/index.js";
function geraStringAleatoria(tamanho) {
var stringAleatoria = "";
var caracteres =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < tamanho; i++) {
stringAleatoria += caracteres.charAt(
Math.floor(Math.random() * caracteres.length)
);
}
return stringAleatoria;
}
const nodeTypes = {
message: messageNode,
start: startNode,
condition: conditionNode,
menu: menuNode,
interval: intervalNode,
img: imgNode,
audio: audioNode,
randomizer: randomizerNode,
video: videoNode,
singleBlock: singleBlockNode,
ticket: ticketNode,
chatController: chatControllerNode,
flowConection: flowConectionNode,
manipulator:manipulatorNode,
};
const edgeTypes = {
buttonedge: RemoveEdge
};
const initialNodes = [
{
id: "1",
position: { x: 250, y: 100 },
data: { label: "Inicio do fluxo" },
type: "start"
}
];
value: data.value
}
,
type: "condition"
}
];
});
}
if (type === "menu") {
return setNodes(old => {
return [
...old,
{
id: geraStringAleatoria(30),
position: { x: posX, y: posY },
data: {
message: data.message,
arrayOption: data.arrayOption
},
type: "menu"
}
];
});
}
if (type === "img") {
return setNodes(old => {
return [
...old,
{
id: geraStringAleatoria(30),
position: { x: posX, y: posY },
data: { url: data.url },
type: "img"
}
];
});
}
if (type === "audio") {
return setNodes(old => {
return [
...old,
{
id: geraStringAleatoria(30),
position: { x: posX, y: posY },
data: { url: data.url, record: data.record },
type: "audio"
}
];
});
}
if (type === "randomizer") {
return setNodes(old => {
return [
...old,
{
id: geraStringAleatoria(30),
position: { x: posX, y: posY },
data: { percent: data.percent },
type: "randomizer"
}
];
});
}
if (type === "video") {
return setNodes(old => {
return [
...old,
{
id: geraStringAleatoria(30),
position: { x: posX, y: posY },
data: { url: data.url },
type: "video"
}
];
});
}
if (type === "singleBlock") {
return setNodes(old => {
return [
...old,
{
id: geraStringAleatoria(30),
position: { x: posX, y: posY },
data: { ...data },
type: "singleBlock"
}
];
});
}
useEffect(() => {
setLoading(true);
const delayDebounceFn = setTimeout(() => {
const fetchContacts = async () => {
try {
const { data } = await api.get(`/flowbuilder/flow/${id}`);
if (data.flow.flow !== null) {
setNodes(data.flow.flow.nodes);
setEdges(data.flow.flow.connections);
}
setLoading(false);
} catch (err) {
toastError(err);
}
};
fetchContacts();
}, 500);
return () => clearTimeout(delayDebounceFn);
}, [id]);
useEffect(() => {
if (storageItems.action === "delete") {
setNodes(old => old.filter(item => item.id !== storageItems.node));
setEdges(old => {
const newData = old.filter(item => item.source !== storageItems.node);
const newClearTarget = newData.filter(
item => item.target !== storageItems.node
);
return newClearTarget;
});
storageItems.setNodesStorage("");
storageItems.setAct("idle");
}
if (storageItems.action === "duplicate") {
const nodeDuplicate = nodes.filter(
item => item.id === storageItems.node
)[0];
const maioresX = nodes.map(node => node.position.x);
const maiorX = Math.max(...maioresX);
const finalY = nodes[nodes.length - 1].position.y;
const nodeNew = {
...nodeDuplicate,
id: geraStringAleatoria(30),
position: {
x: maiorX + 240,
y: finalY
},
selected: false,
style: { backgroundColor: "#555555", padding: 0, borderRadius: 8 }
};
setNodes(old => [...old, nodeNew]);
storageItems.setNodesStorage("");
storageItems.setAct("idle");
}
}, [storageItems.action]);
const actions = [
{
icon: (
<RocketLaunch
sx={{
color: "#3ABA38"
}}
/>
),
name: "Inicio",
type: "start"
},
{
icon: (
<Rule
sx={{ color: "#ADD8E6" }} />
),
name: "Condição",
type: "condition"
},
{
icon: (
<LibraryBooks
sx={{
color: "#EC5858"
}}
/>
),
name: "Conteúdo",
type: "content"
},
{
icon: (
<DynamicFeed
sx={{
color: "#683AC8"
}}
/>
),
name: "Menu",
type: "menu"
},
{
icon: (
<CallSplit
sx={{
color: "#1FBADC"
}}
/>
),
name: "Randomizador",
type: "random"
},
{
icon: (
<AccessTime
sx={{
color: "#F7953B"
}}
/>
),
name: "Intervalo",
type: "interval"
},
{
icon:(<AccountTreeIcon
sx={{
color:"#F7953B"
}}
/>
),
name: "Controlador de chat",
type: "chatController"
},
{
icon: (
<ConfirmationNumber
sx={{
color: "#F7953B"
}}
/>
),
name: "Ticket",
type: "ticket"
},
{
icon: (
<AccountTreeIcon
sx={{
color: "#aa4cf7"
}}
/>
),
name: "Conexção de fluxo",
type: "flowConection"
},
{
icon: (
<ConstructionIcon
sx={{
color: "#DDCF0E"
}}
/>
),
name: "Manipulador",
type: "manipulator"
},
];
return (
<Stack sx={{ height: "100vh" }}>
<FlowBuilderAddTextModal
open={modalAddText}
onSave={textAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddText}
/>
<FlowBuilderIntervalModal
open={modalAddInterval}
onSave={intervalAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddInterval}
/>
<FlowBuilderConditionModal
open={modalAddCondition}
onSave={conditionAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddCondition}
/>
<FlowBuilderManipulatorModal
open={modalAddManipulator}
onSave={manipulatorAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddManipulator}
/>
<FlowBuilderMenuModal
open={modalAddMenu}
onSave={menuAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddMenu}
/>
<FlowBuilderAddImgModal
open={modalAddImg}
onSave={imgAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddImg}
/>
<FlowBuilderAddAudioModal
open={modalAddAudio}
onSave={audioAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddAudio}
/>
<FlowBuilderRandomizerModal
open={modalAddRandomizer}
onSave={randomizerAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddRandomizer}
/>
<FlowBuilderAddVideoModal
open={modalAddVideo}
onSave={videoAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddVideo}
/>
<FlowBuilderSingleBlockModal
open={modalAddSingleBlock}
onSave={singleBlockAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddSingleBlock}
/>
<FlowBuilderTicketModal
open={modalAddTicket}
onSave={ticketAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddTicket}
/>
<FlowBuilderChatControllerModal
open={modalAddChatController}
onSave={chatControllerAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddChatController}
<FlowBuilderAddFlowConection
open={modalAddFlowConection}
onSave={flowConectionAdd}
data={dataNode}
onUpdate={updateNode}
close={setModalAddFlowConection}
/>
<MainHeader>
<Title>Desenhe seu fluxo</Title>
</MainHeader>
{!loading && (
<Paper
className={classes.mainPaper}
variant="outlined"
onScroll={handleScroll}
>
<Stack>
<SpeedDial
ariaLabel="SpeedDial basic example"
sx={{
position: "absolute",
top: 16,
left: 16,
".MuiSpeedDial-fab": {
backgroundColor: colorPrimary(),
}
}}
icon={<SpeedDialIcon />}
direction={"down"}
onClick={() => setOpen(prevOpen => !prevOpen)}
open={open}
>
{actions.map(action => (
<SpeedDialAction
key={action.name}
icon={action.icon}
tooltipTitle={action.name}
tooltipOpen
tooltipPlacement={"right"}
sx={{
'& .MuiSpeedDialAction-staticTooltipLabel': {
width: 'max-content',
},
}}
onClick={() => clickActions(action.type)}
/>
))}
</SpeedDial>
</Stack>
<Stack
sx={{
position: "absolute",
justifyContent: "center",
flexDirection: "row",
width: "100%"
}}
>
<Typography
style={{ color: "#010101", textShadow: "#010101 1px 0 10px" }}
>
Não se esqueça de salvar seu fluxo!
</Typography>
</Stack>
<Stack direction={"row"} justifyContent={"end"}>
<Button
sx={{ textTransform: "none" }}
variant="contained"
color="primary"
onClick={() => saveFlow()}
>
Salvar
</Button>
</Stack>
<Stack
direction={"row"}
style={{
width: "100%",
height: "90%",
position: "relative",
display: "flex"
}}
>
<ReactFlow
nodes={nodes}
edges={edges}
deleteKeyCode={["Backspace", "Delete"]}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeDoubleClick={doubleClick}
onNodeClick={clickNode}
onEdgeClick={clickEdge}
onConnect={onConnect}
nodeTypes={nodeTypes}
fitView
connectionLineStyle={connectionLineStyle}
style={{
//backgroundImage: `url(${imgBackground})`,
//backgroundSize: "cover"
backgroundColor: "#F8F9FA"
}}
edgeTypes={edgeTypes}
variant={"cross"}
defaultEdgeOptions={{
style: { color: "#ff0000", strokeWidth: "6px" },
animated: false
}}
>
<Controls />
<MiniMap />
<Background variant="dots" gap={12} size={-1} />
</ReactFlow>
<Stack
style={{
backgroundColor: "#FAFAFA",
height: "20px",
width: "58px",
position: "absolute",
bottom: 0,
right: 0,
zIndex: 1111
}}
/>
{/* <Stack
style={{
backgroundColor: "#1B1B1B",
height: "70%",
width: "150px",
position: "absolute",
left: 0,
top: 0,
zIndex: 1111,
borderRadius: 3,
padding: 8
}}
spacing={1}
>
<Typography style={{ color: "#ffffff", textAlign: "center" }}>
Adicionar
</Typography>
<Button
onClick={() => addNode("start")}
variant="contained"
style={{
backgroundColor: "#3ABA38",
color: "#ffffff",
padding: 8,
"&:hover": {
backgroundColor: "#3e3b7f"
},
textTransform: "none"
}}
>
<RocketLaunch
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Inicio
</Button>
<Button
onClick={() => setModalAddText("create")}
variant="contained"
style={{
backgroundColor: "#6865A5",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<Message
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Texto
</Button>
<Button
onClick={() => setModalAddInterval("create")}
variant="contained"
style={{
backgroundColor: "#F7953B",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<AccessTime
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Intervalo
</Button>
<Button
onClick={() => setModalAddCondition("create")}
variant="contained"
disabled
style={{
backgroundColor: "#524d4d",
color: "#cccaed",
padding: 8,
textTransform: "none"
}}
>
<ImportExport
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Condição
</Button>
<Button
onClick={() => setModalAddMenu("create")}
variant="contained"
style={{
backgroundColor: "#683AC8",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<DynamicFeed
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Menu
</Button>
<Button
onClick={() => setModalAddAudio("create")}
variant="contained"
style={{
backgroundColor: "#6865A5",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<MicNone
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Audio
</Button>
<Button
onClick={() => setModalAddVideo("create")}
variant="contained"
style={{
backgroundColor: "#6865A5",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<Videocam
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Video
</Button>
<Button
onClick={() => setModalAddImg("create")}
variant="contained"
style={{
backgroundColor: "#6865A5",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<Image
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Imagem
</Button>
<Button
onClick={() => setModalAddRandomizer("create")}
variant="contained"
style={{
backgroundColor: "#1FBADC",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<CallSplit
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Randomizador
</Button>
<Button
onClick={() => setModalAddSingleBlock("create")}
variant="contained"
style={{
backgroundColor: "#EC5858",
color: "#ffffff",
padding: 8,
textTransform: "none"
}}
>
<LibraryBooks
sx={{
width: "16px",
height: "16px",
marginRight: "4px"
}}
/>
Conteúdo
</Button>
</Stack> */}
</Stack>
</Paper>
)}
{loading && (
<Stack justifyContent={"center"} alignItems={"center"} height={"70vh"}>
<CircularProgress />
</Stack>
)}
</Stack>
);
};