/* * Copyright © 2024 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ import BrokenImageOutlinedIcon from "@mui/icons-material/BrokenImageOutlined"; import ChatBubbleOutlineOutlinedIcon from "@mui/icons-material/ChatBubbleOutlineOutlined"; import ExtensionOutlinedIcon from "@mui/icons-material/ExtensionOutlined"; import FastForwardRoundedIcon from "@mui/icons-material/FastForwardRounded"; import MenuRoundedIcon from "@mui/icons-material/MenuRounded"; import PlayArrowRoundedIcon from "@mui/icons-material/PlayArrowRounded"; import ReplyIcon from "@mui/icons-material/Reply"; import { Chip, styled } from "@mui/material"; import { DiagramEngine, PortWidget } from "@projectstorm/react-diagrams-core"; import clsx from "clsx"; import * as React from "react"; import { FC } from "react"; import { withTranslation, WithTranslation } from "react-i18next"; import { UnifiedIcon } from "@/app-components/icons/UnifiedIcon"; import AttachmentIcon from "@/app-components/svg/toolbar/AttachmentIcon"; import ButtonsIcon from "@/app-components/svg/toolbar/ButtonsIcon"; import ListIcon from "@/app-components/svg/toolbar/ListIcon"; import PluginIcon from "@/app-components/svg/toolbar/PluginIcon"; import QuickRepliesIcon from "@/app-components/svg/toolbar/QuickRepliesIcon"; import SimpleTextIcon from "@/app-components/svg/toolbar/SimpleTextIcon"; import TriggerIcon from "@/app-components/svg/TriggerIcon"; import { IBlockFull, Pattern } from "@/types/block.types"; import { BlockPorts, BlockTypes, TBlock } from "@/types/visual-editor.types"; import { truncate } from "@/utils/text"; import { NodeModel } from "./NodeModel"; export const BLOCK_WIDTH = 360; export const BLOCK_HEIGHT = 200; export interface NodeWidgetProps { node: NodeModel; engine: DiagramEngine; color: string; } const IconContainer = styled("div")(({ theme }) => ({ display: "flex", justifyContent: "center", alignItems: "center", backgroundColor: theme.palette.background.paper, borderRadius: "100%", padding: "1px", })); export interface NodeWidgetState {} class NodeAbstractWidget extends React.Component< NodeWidgetProps & WithTranslation, NodeWidgetState > { constructor(props: NodeWidgetProps & WithTranslation) { super(props); this.state = {}; } render() { return (
{!this.props.node.source ? ( ) : null} {this.props.node.content}
{Object.entries(this.props.node.getPorts()) //TODO: need to be updated // @ts-ignore .filter(([, value]) => !value.options.in) .map(([, value], index) => ( {index === 0 ? ( ) : ( )} ))}
); } } class NodeFunctionWidget extends React.Component< NodeWidgetProps & WithTranslation, NodeWidgetState > { constructor(props: NodeWidgetProps & WithTranslation) { super(props); this.state = {}; } render() { const { t } = this.props; return ( <> {this.props.node.inputs.length > 0 ? (
{t("label.inputs")}
) : null} {this.props.node.inputs.map((input, index) => (
{input}
))} {this.props.node.outputs.length > 0 ? (
Outputs
) : null} {this.props.node.outputs.map((output, index) => (
{output}
))} ); } } function determineCase(blockMessage: IBlockFull["message"]) { if (typeof blockMessage === "string" || Array.isArray(blockMessage)) return "text"; if ("attachment" in blockMessage) return "attachment"; if ("quickReplies" in blockMessage) return "quickReplies"; if ("buttons" in blockMessage) return "buttons"; if ("elements" in blockMessage) return "list"; return "plugin"; } const getBlockConfig = ( blockMessage: IBlockFull["message"], ): { type: TBlock; color: string; Icon: FC> } => { switch (determineCase(blockMessage)) { case "text": return { type: "text", color: "#009185", Icon: SimpleTextIcon }; case "attachment": return { type: "attachment", color: "#e6a23c", Icon: AttachmentIcon }; case "quickReplies": return { type: "quickReplies", color: "#a80551", Icon: QuickRepliesIcon }; case "buttons": return { type: "buttons", color: "#570063", Icon: ButtonsIcon }; case "list": return { type: "list", color: "#108aa8", Icon: ListIcon }; case "plugin": return { type: "plugin", color: "#a8ba33", Icon: PluginIcon }; default: throw new Error("Unexpected case"); } }; class NodeWidget extends React.Component< NodeWidgetProps & WithTranslation, NodeWidgetState > { config: { type: TBlock; color: string; Icon: FC>; }; constructor(props: NodeWidgetProps & WithTranslation) { super(props); this.state = {}; this.config = getBlockConfig(this.props.node.message as any); } render() { const { t, i18n, tReady } = this.props; return (
{this.props.node.starts_conversation ? (
) : null}
{this.props.node.title}
{this.props.node.patterns.length > 0 ? ( this.props.node.patterns .map((pattern: Pattern) => { if (typeof pattern === "string") { return pattern; } else if (typeof pattern === "object") { if (pattern && "label" in pattern) { return pattern.label; } else if (Array.isArray(pattern)) { return pattern .map((p) => { return `${p.entity}=${"value" in p ? p.value : "*"}`; }) .join(" & "); } } }) .map((p, idx) => ( )) ) : ( {t("label.no_patterns")} )}
{[BlockTypes.attachment].includes(BlockTypes[this.config.type]) ? (
{t("label.attachment")}:{" "} {(this.props.node.message as any).attachment.type}
) : null} {[BlockTypes.plugin].includes(BlockTypes[this.config.type]) ? (
Plugin: {(this.props.node.message as any).plugin}
) : null} {[BlockTypes.list].includes(BlockTypes[this.config.type]) ? (
{t("label.list")}
) : null} {[BlockTypes.text].includes(BlockTypes[this.config.type]) && this.props.node.message.length ? (
{truncate(this.props.node.message[0])}
) : null} {[BlockTypes.quickReplies, BlockTypes.buttons].includes( BlockTypes[this.config.type], ) ? (
{ //TODO: need to be updated // @ts-ignore truncate(this.props.node.message.text) }
) : null} {[BlockTypes.quickReplies, BlockTypes.buttons].includes( BlockTypes[this.config.type], ) ? (
{ //TODO: need to be updated // @ts-ignore ( (this.props.node.message as any).buttons || (this.props.node.message as any).quickReplies ).map((button, idx: number) => ( )) }
) : null}{" "}
{this.props.node.content ? ( ) : ( )}
); } } export default withTranslation()(NodeWidget);