flow 2

 import React, { useState, useCallback, useEffect, useRef } from "react";

import Layout from "@/components/layout";

import { Combobox } from "@/components/ui/combobox";

import {

  ReactFlow,

  MiniMap,

  Controls,

  Background,

  useNodesState,

  useEdgesState,

  addEdge,

  Handle,

  Position,

  ReactFlowProvider,

  getBezierPath,

} from "@xyflow/react";

import Spinner from "@/components/spinner";

import { getCookie } from "cookies-next";

import "@xyflow/react/dist/style.css";

import toast, { Toaster } from "react-hot-toast";

import { X, Image, List, Type, Database } from "lucide-react";

import axios from "../../../utils/axiosInstance";

import { useRouter } from "next/router";

import { uploadFileToS3 } from "../../../utils/mediaupload";

import { generateHandleId } from "../../../utils/handleIds";


const CustomEdge = ({

  id,

  sourceX,

  sourceY,

  targetX,

  targetY,

  sourcePosition,

  targetPosition,

  style = {},

  data,

  markerEnd,

}) => {

  const [edgePath, labelX, labelY] = getBezierPath({

    sourceX,

    sourceY,

    sourcePosition,

    targetX,

    targetY,

    targetPosition,

  });


  const [isHovered, setIsHovered] = useState(false);


  // Ensure we're using the onDelete callback from props.data

  const handleEdgeDelete = () => {

    if (data?.onDelete) {

      data.onDelete(id);

    }

  };


  return (

    <>

      <path

        id={id}

        style={{

          ...style,

          strokeWidth: 2,

          stroke: "#6366f1",

        }}

        className="react-flow__edge-path hover:stroke-indigo-400"

        d={edgePath}

        markerEnd={markerEnd}

      />

      <g

        transform={`translate(${labelX} ${labelY})`}

        onMouseEnter={() => setIsHovered(true)}

        onMouseLeave={() => setIsHovered(false)}

      >

        {/* Make the entire circle clickable */}

        <circle

          r="18"

          className={`

            cursor-pointer

            transition-all duration-200 

            ${

              isHovered

                ? "fill-red-50 stroke-red-300"

                : "fill-white stroke-gray-200"

            }

          `}

          strokeWidth="1.5"

          onClick={handleEdgeDelete} // Make sure this is called

          style={{ pointerEvents: "all" }} // Ensure clicks are captured

        />


        {/* X icon */}

        <g

          className={`

            transform transition-transform duration-200

            ${isHovered ? "scale-110" : "scale-100"}

          `}

          style={{ pointerEvents: "none" }} // Prevent X from capturing clicks

        >

          <path

            d="M-6 -6 L6 6 M-6 6 L6 -6"

            className={`

              transition-all duration-200

              ${isHovered ? "stroke-red-500" : "stroke-gray-400"}

            `}

            strokeWidth="2.5"

            strokeLinecap="round"

          />

        </g>


        {/* Ripple effect on hover */}

        {isHovered && (

          <circle

            r="18"

            className="fill-none stroke-red-200 animate-ping"

            strokeWidth="2"

            opacity="0.5"

            style={{ pointerEvents: "none" }} // Prevent ripple from capturing clicks

          />

        )}

      </g>

    </>

  );

};


const NodeTypeDropdown = ({ options, onSelect, onCancel, position }) => {

  const nodeIcons = {

    "Text + Button": Type,

    Media: Image,

    List: List,

    Attribute: Database,

  };


  return (

    <div

      className="absolute bg-white rounded-xl shadow-2xl border border-gray-200 p-4 z-50 w-[300px]"

      style={{

        top: position.y,

        left: position.x,

        transform: "translate(-50%, -50%)",

      }}

    >

      <div className="flex justify-between items-center mb-4">

        <h3 className="text-lg font-semibold text-gray-800">Select Action</h3>

        <button

          onClick={onCancel}

          className="text-gray-500 hover:text-gray-700 transition-colors"

          title="Cancel"

        >

          <X className="w-6 h-6" />

        </button>

      </div>


      <div className="grid grid-cols-2 gap-3">

        {options.map((option) => {

          const Icon = nodeIcons[option];

          return (

            <button

              key={option}

              onClick={() => onSelect(option)}

              className="

                flex flex-col items-center justify-center 

                p-4 rounded-lg 

                hover:bg-indigo-50 

                border border-transparent 

                hover:border-indigo-200 

                transition-all 

                group

              "

            >

              <Icon

                className="

                  w-8 h-8 mb-2 

                  text-gray-500 

                  group-hover:text-indigo-600 

                  transition-colors

                "

              />

              <span

                className="

                text-sm font-medium 

                text-gray-700 

                group-hover:text-indigo-700

              "

              >

                {option}

              </span>

            </button>

          );

        })}

      </div>

    </div>

  );

};


// Common Header Component for Nodes


const NodeHeader = ({

  type,

  onDelete,

  canDelete = true,

  messageNumber = null,

}) => (

  <div className="flex items-center justify-between mb-3">

    <div className="flex items-center gap-3">

      {/* WhatsApp Icon */}

      <svg

        viewBox="0 0 24 24"

        className="w-6 h-6 text-[#25D366] flex-shrink-0"

        fill="currentColor"

      >

        <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" />

      </svg>


      {/* Node Type */}

      <span className="text-[15px] font-medium text-gray-700">{type}</span>


      {/* Message Number Badge */}

      {messageNumber && (

        <span className="bg-indigo-100 text-indigo-700 text-sm font-medium px-3 py-0.5 rounded-full">

          Message {messageNumber}

        </span>

      )}

    </div>


    {/* Delete Button */}

    {canDelete && (

      <button

        onClick={onDelete}

        className="text-gray-400 hover:text-red-500 transition-colors"

        title="Delete node"

      >

        <svg

          xmlns="http://www.w3.org/2000/svg"

          className="h-5 w-5"

          viewBox="0 0 20 20"

          fill="currentColor"

        >

          <path

            fillRule="evenodd"

            d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"

            clipRule="evenodd"

          />

        </svg>

      </button>

    )}

  </div>

);


// Button Component with Delete Option

const ButtonInput = ({

  button,

  onChange,

  onDelete,

  handleId,

  showHandle = true,

}) => (

  <div className="relative flex items-center gap-2">

    <input

      type="text"

      value={button.label}

      onChange={(e) => onChange(e.target.value)}

      className="w-full p-2 border rounded-md text-sm"

      placeholder="Button text"

    />

    <button

      onClick={onDelete}

      className="text-gray-400 hover:text-red-500 transition-colors"

      title="Delete button"

    >

      <svg

        xmlns="http://www.w3.org/2000/svg"

        className="h-5 w-5"

        viewBox="0 0 20 20"

        fill="currentColor"

      >

        <path

          fillRule="evenodd"

          d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"

          clipRule="evenodd"

        />

      </svg>

    </button>

    {showHandle && (

      <Handle

        type="source"

        position={Position.Right}

        id={handleId}

        style={{

          backgroundColor: "indigo",

          width: "13px",

          height: "13px",

          borderRadius: "50%",

          right: "-20px",

        }}

      />

    )}

  </div>

);


// TriggerNode component

const TriggerNode = ({ data, id }) => {

  const [keywords, setKeywords] = useState(data.keywords || ["Hi"]);


  const handleAddKeyword = () => {

    const newKeywords = [...keywords, "New Keyword"];

    setKeywords(newKeywords);

    data.onKeywordsUpdate?.(id, newKeywords);

  };


  const handleKeywordChange = (index, value) => {

    const newKeywords = keywords.map((kw, i) => (i === index ? value : kw));

    setKeywords(newKeywords);

    data.onKeywordsUpdate?.(id, newKeywords);

  };


  const handleDeleteKeyword = (index) => {

    const newKeywords = keywords.filter((_, i) => i !== index);

    setKeywords(newKeywords);

    data.onKeywordsUpdate?.(id, newKeywords);

  };


  return (

    <div className="bg-white rounded-lg shadow-lg p-4 w-[300px] relative">

      <NodeHeader type="Starting Step" canDelete={false} />


      <div className="bg-gray-50 rounded-lg p-3 space-y-3">

        {/* Add descriptive text */}

        <div className="text-xs text-gray-600 mb-3">

          Start the flow if the user's message matches any of these keywords:

        </div>


        {keywords.map((keyword, index) => (

          <div key={index} className="relative flex items-center gap-2">

            <input

              type="text"

              value={keyword}

              onChange={(e) => handleKeywordChange(index, e.target.value)}

              className="w-full p-2 border rounded-md text-sm"

              placeholder="Enter keyword"

            />

            <button

              onClick={() => handleDeleteKeyword(index)}

              className="text-gray-400 hover:text-red-500 transition-colors"

              title="Delete keyword"

            >

              <svg

                xmlns="http://www.w3.org/2000/svg"

                className="h-5 w-5"

                viewBox="0 0 20 20"

                fill="currentColor"

              >

                <path

                  fillRule="evenodd"

                  d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"

                  clipRule="evenodd"

                />

              </svg>

            </button>

          </div>

        ))}


        <button

          onClick={handleAddKeyword}

          className="text-indigo-500 hover:text-indigo-700 text-sm"

        >

          + Add Keyword

        </button>


        {/* Add note for additional context */}

        {/* <div className="text-xs text-gray-500 mt-2 italic">

          Note: The flow will start automatically when a user sends any of these

          keywords.

        </div> */}

      </div>


      {/* Bottom handle for connecting to other nodes */}

      <Handle

        type="source"

        position={Position.Right}

        style={{

          backgroundColor: "indigo",

          width: "13px",

          height: "13px",

          borderRadius: "50%",

        }}

      />

    </div>

  );

};


// Text + Button Node Component

const TextButtonNode = ({ data, id }) => {

  const [content, setContent] = useState(data.content || "");


  const defaultButton = {

    label: "Button 1",

    handleId: generateHandleId("btn", Date.now(), 0),

  };


  const [buttons, setButtons] = useState(

    data.buttons?.length > 0 ? data.buttons : [defaultButton]

  );


  // In TextButtonNode component

  useEffect(() => {

    if (!data.buttons || data.buttons.length === 0) {

      data.onButtonsUpdate?.(id, [defaultButton]);

    }

  }, []);


  const handleContentChange = (value) => {

    // Limit content to 1024 characters (WhatsApp body text limit)

    if (value.length <= 1024) {

      setContent(value);

      data.onContentUpdate?.(id, value);

    }

  };


  const handleAddButton = () => {

    if (buttons.length < 3) {

      const newButton = {

        label: "New Button",

        handleId: generateHandleId("btn", Date.now(), buttons.length),

      };

      const updatedButtons = [...buttons, newButton];

      setButtons(updatedButtons);

      data.onButtonsUpdate?.(id, updatedButtons);

    }

  };


  const handleButtonTextChange = (index, value) => {

    // Limit button text to 20 characters

    if (value.length <= 20) {

      const newButtons = buttons.map((btn, i) =>

        i === index ? { ...btn, label: value } : btn

      );

      setButtons(newButtons);

      data.onButtonsUpdate?.(id, newButtons);

    }

  };


  const handleDeleteButton = (index) => {

    if (buttons.length <= 1) {

      toast.error("At least one button is required");

      return;

    }

    const newButtons = buttons.filter((_, i) => i !== index);

    setButtons(newButtons);

    data.onButtonsUpdate?.(id, newButtons);

  };


  return (

    <div className="bg-white rounded-lg shadow-lg p-4 w-[300px] relative">

      <Handle

        type="target"

        position={Position.Left}

        style={{

          backgroundColor: "indigo",

          width: "13px",

          height: "13px",

          borderRadius: "50%",

        }}

      />


      <NodeHeader

        type="Text + Button"

        onDelete={data.onDelete}

        messageNumber={data.messageNumber}

      />


      <div className="bg-gray-50 rounded-lg p-3 space-y-3">

        <div>

          <textarea

            value={content}

            onChange={(e) => handleContentChange(e.target.value)}

            placeholder="Enter your message... (max 1024 chars)"

            className="w-full p-2 border rounded-md text-sm min-h-[90px] resize-none"

          />

          <div className="text-xs text-gray-500 text-right">

            {content.length}/1024 characters

          </div>

        </div>


        {buttons.map((btn, index) => (

          <div key={index} className="relative flex items-center gap-2">

            <input

              type="text"

              value={btn.label}

              onChange={(e) => handleButtonTextChange(index, e.target.value)}

              className="w-full p-2 border rounded-md text-sm"

              placeholder="Button text (max 20 chars)"

            />

            <div className="text-xs text-gray-500 mr-2">

              {btn.label.length}/20

            </div>

            <button

              onClick={() => handleDeleteButton(index)}

              className="text-gray-400 hover:text-red-500 transition-colors"

              title="Delete button"

            >

              <svg

                xmlns="http://www.w3.org/2000/svg"

                className="h-5 w-5"

                viewBox="0 0 20 20"

                fill="currentColor"

              >

                <path

                  fillRule="evenodd"

                  d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"

                  clipRule="evenodd"

                />

              </svg>

            </button>

            <Handle

              type="source"

              position={Position.Right}

              id={btn.handleId}

              style={{

                backgroundColor: "indigo",

                width: "13px",

                height: "13px",

                borderRadius: "50%",

                right: "-20px",

              }}

            />

          </div>

        ))}


        <button

          onClick={handleAddButton}

          className="text-indigo-500 hover:text-indigo-700 text-sm"

          disabled={buttons.length >= 3}

        >

          + Add Button {buttons.length}/3

        </button>

      </div>

    </div>

  );

};


// Media Node Component

const MediaNode = ({ data, id }) => {

  const [content, setContent] = useState(data.content || "");

  const [mediaUrl, setMediaUrl] = useState(data.mediaUrl || "");

  const [mediaType, setMediaType] = useState(data.mediaType || "");

  const [fileName, setFileName] = useState(data.fileName || "");

  const defaultButton = {

    label: "Button 1",

    handleId: generateHandleId("btn", Date.now(), 0), // Generate unique ID immediately

  };


  const [buttons, setButtons] = useState(

    data.buttons?.length > 0 ? data.buttons : [defaultButton]

  );

  const [isLoading, setIsLoading] = useState(false);

  const [uploadedId, setUploadedId] = useState(data.uploadedId || "");


  useEffect(() => {

    if (!data.buttons || data.buttons.length === 0) {

      data.onButtonsUpdate?.(id, [defaultButton]);

    }

  }, []);


  const getMediaType = (file) => {

    if (file.type.startsWith("image/")) return "image";

    if (file.type.startsWith("video/")) return "video";

    return "document";

  };


  const handleMediaUpload = async (event) => {

    const file = event.target.files[0];


    if (file) {

      setIsLoading(true);


      try {

        // Upload to S3

        const result = await uploadFileToS3(

          file,

          `whatsapp-media-${id}`, // Using node id to make it unique

          "whatsapp/automation"

        );


        if (result.success) {

          let mediaPreviewUrl = null;

          // Create preview URL for images and videos

          if (result.category === "image" || result.category === "video") {

            mediaPreviewUrl = result.url; // Use the S3 URL directly

          }


          // Set all the state values

          setUploadedId(result.url); // Store S3 URL instead of WhatsApp media ID

          setFileName(result.fileName);

          setMediaUrl(mediaPreviewUrl || result.url);

          setMediaType(result.category);


          // Update node data with all fields

          data.onMediaUpdate?.(

            id,

            result.url, // Use S3 URL

            result.category,

            result.url, // Using S3 URL as ID

            result.fileName

          );


          toast.success("Media uploaded successfully");

        }

      } catch (error) {

        console.error("File upload failed:", error);

        toast.error(

          error.message || "Failed to upload media. Please try again."

        );

      } finally {

        setIsLoading(false);

      }

    }

  };


  // const handleMediaUpload = async (event) => {

  //   const file = event.target.files[0];


  //   if (file) {

  //     setIsLoading(true);

  //     const formData = new FormData();

  //     formData.append("file", file);

  //     formData.append("messaging_product", "whatsapp");


  //     const type = getMediaType(file);

  //     formData.append("type", type);


  //     const access_token = getCookie("at");

  //     const wb_phone_number_id = getCookie("pni");


  //     try {

  //       const response = await fetch(

  //         `https://graph.facebook.com/v21.0/${wb_phone_number_id}/media`,

  //         {

  //           method: "POST",

  //           body: formData,

  //           headers: {

  //             Authorization: `Bearer ${access_token}`,

  //           },

  //         }

  //       );


  //       if (response.ok) {

  //         const responseData = await response.json();

  //         console.log(responseData, "this is media id data");


  //         let mediaPreviewUrl = null;

  //         // Create preview URL for images and videos

  //         if (type === "image" || type === "video") {

  //           mediaPreviewUrl = URL.createObjectURL(file);

  //         }


  //         // Set all the state values

  //         setUploadedId(responseData.id);

  //         setFileName(file.name);

  //         setMediaUrl(mediaPreviewUrl || "");

  //         setMediaType(type);


  //         // Update node data with all fields

  //         data.onMediaUpdate?.(

  //           id,

  //           mediaPreviewUrl, // Use the mediaPreviewUrl we just created

  //           type,

  //           responseData.id, // Pass the uploaded ID

  //           file.name // Pass the file name

  //         );

  //       } else {

  //         const errorData = await response.json();

  //         toast.error(errorData.error.error_data.details);

  //       }

  //     } catch (error) {

  //       console.error("File upload failed:", error);

  //       toast.error("Failed to upload media. Please try again.");

  //     } finally {

  //       setIsLoading(false);

  //     }

  //   }

  // };


  const renderMediaPreview = () => {

    if (isLoading) {

      return (

        <div className="flex items-center justify-center p-4">

          <Spinner />

          <span className="ml-2 text-sm text-gray-600">Uploading...</span>

        </div>

      );

    }


    if (mediaType === "document") {

      return (

        <div className="relative w-full bg-white p-4 rounded-lg border">

          <div className="flex items-center max-w-full">

            <svg

              className="w-8 h-8 text-gray-500 flex-shrink-0"

              fill="none"

              stroke="currentColor"

              viewBox="0 0 24 24"

              xmlns="http://www.w3.org/2000/svg"

            >

              <path

                strokeLinecap="round"

                strokeLinejoin="round"

                strokeWidth={2}

                d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"

              />

            </svg>

            <div className="ml-3 min-w-0 flex-1">

              <p className="text-sm font-medium text-gray-900 truncate max-w-[180px]">

                {fileName}

              </p>

              <p className="text-sm text-gray-500">Document</p>

            </div>

          </div>

          <button

            onClick={() => {

              setMediaUrl("");

              setMediaType("");

              setUploadedId("");

              setFileName("");

            }}

            className="absolute top-1 right-1 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm hover:bg-red-600"

          >

            ×

          </button>

        </div>

      );

    }


    if (mediaUrl) {

      return (

        <div className="relative w-full">

          {mediaType === "image" ? (

            <img

              src={mediaUrl}

              alt="Uploaded media"

              className="max-w-full h-auto rounded"

            />

          ) : (

            <video

              src={mediaUrl}

              controls

              className="max-w-full h-auto rounded"

            />

          )}

          <button

            onClick={() => {

              setMediaUrl("");

              setMediaType("");

              setUploadedId("");

              setFileName("");

            }}

            className="absolute top-1 right-1 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm hover:bg-red-600"

          >

            ×

          </button>

        </div>

      );

    }


    return (

      <div className="text-center">

        <input

          type="file"

          accept="image/*,video/*,application/*,.pdf,.doc,.docx,.xls,.xlsx"

          onChange={handleMediaUpload}

          className="hidden"

          id={`media-upload-${id}`}

        />

        <label

          htmlFor={`media-upload-${id}`}

          className="cursor-pointer bg-indigo-500 text-white px-4 py-2 rounded-md hover:bg-indigo-600 inline-block"

        >

          Upload Media

        </label>

      </div>

    );

  };


  // Rest of the component remains the same as in the previous implementation

  const handleContentChange = (value) => {

    // Limit content to 1024 characters

    if (value.length <= 1024) {

      setContent(value);

      data.onContentUpdate?.(id, value);

    }

  };


  const handleAddButton = () => {

    if (buttons.length < 3) {

      const newButton = {

        label: "New Button",

        handleId: generateHandleId("btn", Date.now(), buttons.length),

      };

      const updatedButtons = [...buttons, newButton];

      setButtons(updatedButtons);

      data.onButtonsUpdate?.(id, updatedButtons);

    }

  };


  const handleButtonTextChange = (index, value) => {

    // Limit button text to 20 characters

    if (value.length <= 20) {

      const newButtons = buttons.map((btn, i) =>

        i === index ? { ...btn, label: value } : btn

      );

      setButtons(newButtons);

      data.onButtonsUpdate?.(id, newButtons);

    }

  };


  const handleDeleteButton = (index) => {

    if (buttons.length <= 1) {

      toast.error("At least one button is required");

      return;

    }

    const newButtons = buttons.filter((_, i) => i !== index);

    setButtons(newButtons);

    data.onButtonsUpdate?.(id, newButtons);

  };


  return (

    <div className="bg-white rounded-lg shadow-lg p-4 w-[300px] relative">

      <Handle

        type="target"

        position={Position.Left}

        style={{

          backgroundColor: "indigo",

          width: "13px",

          height: "13px",

          borderRadius: "50%",

        }}

      />


      <NodeHeader

        type="Media"

        onDelete={data.onDelete}

        messageNumber={data.messageNumber}

      />


      <div className="bg-gray-50 rounded-lg p-3 space-y-3">

        <div className="flex justify-center items-center bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg p-4">

          {renderMediaPreview()}

        </div>


        <div>

          <textarea

            value={content}

            onChange={(e) => handleContentChange(e.target.value)}

            placeholder="Enter your message... (max 1024 chars)"

            className="w-full p-2 border rounded-md text-sm min-h-[60px] resize-none"

          />

          <div className="text-xs text-gray-500 text-right">

            {content.length}/1024 characters

          </div>

        </div>


        {buttons.map((btn, index) => (

          <div key={index} className="relative flex items-center gap-2">

            <input

              type="text"

              value={btn.label}

              onChange={(e) => handleButtonTextChange(index, e.target.value)}

              className="w-full p-2 border rounded-md text-sm"

              placeholder="Button text (max 20 chars)"

            />

            <div className="text-xs text-gray-500 mr-2">

              {btn.label.length}/20

            </div>

            <button

              onClick={() => handleDeleteButton(index)}

              className="text-gray-400 hover:text-red-500 transition-colors"

              title="Delete button"

            >

              <svg

                xmlns="http://www.w3.org/2000/svg"

                className="h-5 w-5"

                viewBox="0 0 20 20"

                fill="currentColor"

              >

                <path

                  fillRule="evenodd"

                  d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"

                  clipRule="evenodd"

                />

              </svg>

            </button>

            <Handle

              type="source"

              position={Position.Right}

              id={btn.handleId}

              style={{

                backgroundColor: "indigo",

                width: "13px",

                height: "13px",

                borderRadius: "50%",

                right: "-20px",

              }}

            />

          </div>

        ))}


        <button

          onClick={handleAddButton}

          className="text-indigo-500 hover:text-indigo-700 text-sm"

          disabled={buttons.length >= 3}

        >

          + Add Button {buttons.length}/3

        </button>

      </div>

    </div>

  );

};


const ListNode = ({ data, id }) => {

  const [content, setContent] = useState(data.content || "");

  const [buttonText, setButtonText] = useState(data.buttonText || "Options");

  // Default section and row

  const defaultSection = {

    title: "Section 1",

    rows: [

      {

        id: generateHandleId("list-row", Date.now(), "0-0"),

        title: "Option 1",

        description: "Description for Option 1 (Optional)",

      },

    ],

  };


  const [sections, setSections] = useState(

    data.sections?.length > 0 ? data.sections : [defaultSection]

  );


  // Add this useEffect to sync with parent on mount

  useEffect(() => {

    if (!data.sections || data.sections.length === 0) {

      data.onSectionsUpdate?.(id, [defaultSection]);

    }

  }, []); // Empty dependency array means this runs once on mount


  // Helper function to count total rows across all sections

  const getTotalRowCount = useCallback(() => {

    return sections.reduce((total, section) => total + section.rows.length, 0);

  }, [sections]);


  // Content update handler

  const handleContentChange = (value) => {

    if (value.length <= 4096) {

      setContent(value);

      data.onContentUpdate?.(id, value);

    }

  };


  // Button text update handler

  const handleButtonTextChange = (value) => {

    if (value.length <= 20) {

      setButtonText(value);

      data.onButtonTextUpdate?.(id, value);

    }

  };


  // Section handlers

  const handleSectionTitleChange = (sectionIndex, newTitle) => {

    const updatedSections = sections.map((section, index) => {

      if (index === sectionIndex) {

        return { ...section, title: newTitle };

      }

      return section;

    });

    setSections(updatedSections);

    data.onSectionsUpdate?.(id, updatedSections);

  };


  const handleDeleteSection = (sectionIndex) => {

    const totalRowsAfterDeletion =

      getTotalRowCount() - sections[sectionIndex].rows.length;


    if (totalRowsAfterDeletion === 0) {

      toast.error("Must maintain at least one row in total");

      return;

    }


    const updatedSections = sections.filter(

      (_, index) => index !== sectionIndex

    );

    setSections(updatedSections);

    data.onSectionsUpdate?.(id, updatedSections);

  };


  const handleAddSection = () => {

    if (sections.length >= 10) {

      toast.error("Maximum of 10 sections allowed");

      return;

    }


    if (getTotalRowCount() + 1 > 10) {

      toast.error("Maximum of 10 total rows allowed across all sections");

      return;

    }


    const newSection = {

      title: `Section ${sections.length + 1}`,

      rows: [

        {

          id: generateHandleId("list-row", Date.now(), `${sections.length}-0`),

          title: "New Option",

          description: "Description for new option (Optional)",

        },

      ],

    };

    const updatedSections = [...sections, newSection];

    setSections(updatedSections);

    data.onSectionsUpdate?.(id, updatedSections);

  };


  // Row handlers

  const handleRowChange = (sectionIndex, rowIndex, field, value) => {

    const updatedSections = sections.map((section, secIndex) => {

      if (secIndex === sectionIndex) {

        const updatedRows = section.rows.map((row, rowIdx) => {

          if (rowIdx === rowIndex) {

            return { ...row, [field]: value };

          }

          return row;

        });

        return { ...section, rows: updatedRows };

      }

      return section;

    });

    setSections(updatedSections);

    data.onSectionsUpdate?.(id, updatedSections);

  };


  const handleDeleteRow = (sectionIndex, rowIndex) => {

    if (getTotalRowCount() <= 1) {

      toast.error("At least one row is required in total");

      return;

    }


    const updatedSections = sections.map((section, secIndex) => {

      if (secIndex === sectionIndex) {

        const updatedRows = section.rows.filter((_, idx) => idx !== rowIndex);

        return {

          ...section,

          rows: updatedRows.length > 0 ? updatedRows : section.rows,

        };

      }

      return section;

    });

    setSections(updatedSections);

    data.onSectionsUpdate?.(id, updatedSections);

  };


  const handleAddRow = (sectionIndex) => {

    if (getTotalRowCount() >= 10) {

      toast.error("Maximum of 10 total rows allowed across all sections");

      return;

    }


    const updatedSections = sections.map((section, index) => {

      if (index === sectionIndex) {

        const newRow = {

          id: generateHandleId(

            "list-row",

            Date.now(),

            `${sectionIndex}-${section.rows.length}`

          ),

          title: "New Option",

          description: "Description for new option (Optional)",

        };

        return {

          ...section,

          rows: [...section.rows, newRow],

        };

      }

      return section;

    });


    setSections(updatedSections);

    data.onSectionsUpdate?.(id, updatedSections);

  };


  return (

    <div className="bg-white rounded-lg shadow-lg p-4 w-[350px] relative">

      <Handle

        type="target"

        position={Position.Left}

        style={{

          backgroundColor: "#4F46E5",

          width: "13px",

          height: "13px",

          borderRadius: "50%",

        }}

      />


      <NodeHeader

        type="List"

        onDelete={data.onDelete}

        messageNumber={data.messageNumber}

      />


      <div className="bg-gray-50 rounded-lg p-3 space-y-3">

        {/* Content Input */}

        <div>

          <textarea

            value={content}

            onChange={(e) => handleContentChange(e.target.value)}

            placeholder="Message body (max 4096 chars)"

            className="w-full p-2 border rounded-md text-sm min-h-[90px] resize-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"

            maxLength={4096}

          />

          <div className="text-xs text-gray-500 text-right">

            {content.length}/4096 characters

          </div>

        </div>


        {/* Button Text Input */}

        <div>

          <input

            type="text"

            value={buttonText}

            onChange={(e) => handleButtonTextChange(e.target.value)}

            className="w-full p-2 border rounded-md text-sm focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"

            placeholder="Button text (max 20 chars)"

            maxLength={20}

          />

          <div className="text-xs text-gray-500 text-right">

            {buttonText.length}/20 characters

          </div>

        </div>


        {/* Counters */}

        <div className="flex justify-between text-sm text-gray-600 mb-2">

          <span>Sections: {sections.length}/10</span>

          <span>Total Rows: {getTotalRowCount()}/10</span>

        </div>


        {/* Sections */}

        {sections.map((section, sectionIndex) => (

          <div key={sectionIndex} className="border rounded-lg p-2 bg-white">

            {/* Section Header */}

            <div className="flex justify-between items-center mb-2">

              <div className="flex-1 mr-2">

                <input

                  type="text"

                  value={section.title}

                  onChange={(e) => {

                    if (e.target.value.length <= 24) {

                      handleSectionTitleChange(sectionIndex, e.target.value);

                    }

                  }}

                  className="w-full p-2 border rounded-md text-sm focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"

                  placeholder="Section title (max 24 chars)"

                  maxLength={24}

                />

                <div className="text-xs text-gray-500 mt-1">

                  {section.title.length}/24 characters

                </div>

              </div>

              <button

                onClick={() => handleDeleteSection(sectionIndex)}

                className="text-red-500 hover:text-red-700 p-1 rounded-full hover:bg-red-50"

                title="Delete Section"

              >

                <svg

                  className="w-5 h-5"

                  fill="none"

                  stroke="currentColor"

                  viewBox="0 0 24 24"

                >

                  <path

                    strokeLinecap="round"

                    strokeLinejoin="round"

                    strokeWidth={2}

                    d="M6 18L18 6M6 6l12 12"

                  />

                </svg>

              </button>

            </div>


            {/* Rows */}

            {section.rows.map((row, rowIndex) => (

              <div

                key={row.id}

                className="mb-2 p-2 border rounded-lg bg-gray-50 relative"

              >

                <div className="flex mb-2 items-center">

                  <input

                    type="text"

                    value={row.title}

                    onChange={(e) => {

                      if (e.target.value.length <= 24) {

                        handleRowChange(

                          sectionIndex,

                          rowIndex,

                          "title",

                          e.target.value

                        );

                      }

                    }}

                    className="w-full p-2 border rounded-md text-sm mr-2 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"

                    placeholder="Row title (max 24 chars)"

                    maxLength={24}

                  />

                  <div className="text-xs text-gray-500 mr-2">

                    {row.title.length}/24

                  </div>

                  <button

                    onClick={() => handleDeleteRow(sectionIndex, rowIndex)}

                    className="text-red-500 hover:text-red-700 p-1 rounded-full hover:bg-red-50"

                    title="Delete Row"

                  >

                    <svg

                      className="w-5 h-5"

                      fill="none"

                      stroke="currentColor"

                      viewBox="0 0 24 24"

                    >

                      <path

                        strokeLinecap="round"

                        strokeLinejoin="round"

                        strokeWidth={2}

                        d="M6 18L18 6M6 6l12 12"

                      />

                    </svg>

                  </button>

                </div>

                <div>

                  <input

                    type="text"

                    value={row.description}

                    onChange={(e) => {

                      if (e.target.value.length <= 72) {

                        handleRowChange(

                          sectionIndex,

                          rowIndex,

                          "description",

                          e.target.value

                        );

                      }

                    }}

                    className="w-full p-2 border rounded-md text-sm focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"

                    placeholder="Row description (max 72 chars)"

                    maxLength={72}

                  />

                  <div className="text-xs text-gray-500 text-right">

                    {row.description.length}/72 characters

                  </div>

                </div>

                <Handle

                  type="source"

                  position={Position.Right}

                  id={row.id}

                  style={{

                    backgroundColor: "#4F46E5",

                    width: "13px",

                    height: "13px",

                    borderRadius: "50%",

                    right: "-20px",

                    top: "50%",

                    transform: "translateY(-50%)",

                  }}

                />

              </div>

            ))}


            {/* Add Row Button */}

            <button

              onClick={() => handleAddRow(sectionIndex)}

              className={`text-indigo-600 hover:text-indigo-700 text-sm mt-2 flex items-center ${

                getTotalRowCount() >= 10 ? "opacity-50 cursor-not-allowed" : ""

              }`}

              disabled={getTotalRowCount() >= 10}

            >

              <svg

                className="w-4 h-4 mr-1"

                fill="none"

                stroke="currentColor"

                viewBox="0 0 24 24"

              >

                <path

                  strokeLinecap="round"

                  strokeLinejoin="round"

                  strokeWidth={2}

                  d="M12 4v16m8-8H4"

                />

              </svg>

              Add Row

            </button>

          </div>

        ))}


        {/* Add Section Button */}

        <button

          onClick={handleAddSection}

          className={`text-indigo-600 hover:text-indigo-700 text-sm flex items-center justify-center w-full py-2 border-2 border-dashed border-indigo-200 rounded-lg hover:bg-indigo-50 transition-colors ${

            sections.length >= 10 || getTotalRowCount() >= 10

              ? "opacity-50 cursor-not-allowed"

              : ""

          }`}

          disabled={sections.length >= 10 || getTotalRowCount() >= 10}

        >

          <svg

            className="w-4 h-4 mr-1"

            fill="none"

            stroke="currentColor"

            viewBox="0 0 24 24"

          >

            <path

              strokeLinecap="round"

              strokeLinejoin="round"

              strokeWidth={2}

              d="M12 4v16m8-8H4"

            />

          </svg>

          Add Section

        </button>

      </div>

    </div>

  );

};


// Attribute Node Component

const AttributeNode = ({ data, id }) => {

  const [attributes, setAttributes] = useState([]);

  const [selectedAttribute, setSelectedAttribute] = useState(

    data.attribute1 || ""

  );

  const [predefinedValue, setPredefinedValue] = useState(data.attribute2 || "");


  // Fetch attributes only once when the component is first created

  useEffect(() => {

    const fetchAttributes = async () => {

      try {

        const response = await axios.get(

          process.env.NEXT_PUBLIC_BASE_URL + "/whatsapp/attribute/"

        );

        setAttributes(response.data?.results || []);

      } catch (err) {

        toast.error("Failed to load attributes");

      }

    };


    // Only fetch if attributes haven't been loaded yet

    if (attributes.length === 0) {

      fetchAttributes();

    }

  }, []);


  // Effect to update predefined value based on source node's data

  useEffect(() => {

    // Check for button text from various node types

    if (data.sourceButtonText) {

      setPredefinedValue(data.sourceButtonText);

      data.onAttributeUpdate?.(id, "attribute2", data.sourceButtonText);

    }

  }, [data.sourceButtonText]);


  const handleAttributeChange = (value) => {

    setSelectedAttribute(value);

    data.onAttributeUpdate?.(id, "attribute1", value);

  };


  const handlePredefinedValueChange = (value) => {

    setPredefinedValue(value);

    data.onAttributeUpdate?.(id, "attribute2", value);

  };


  return (

    <div className="bg-white rounded-lg shadow-lg p-4 w-[350px] relative">

      {/* Left handle for incoming connections */}


      <NodeHeader

        type="Attribute"

        onDelete={data.onDelete}

        messageNumber={data.messageNumber}

      />

      <Handle

        type="target"

        position={Position.Left}

        style={{

          backgroundColor: "indigo",

          width: "13px",

          height: "13px",

          borderRadius: "50%",

          left: "-6px",

        }}

      />


      {/* Right handle for outgoing connections */}

      <Handle

        type="source"

        position={Position.Right}

        style={{

          backgroundColor: "indigo",

          width: "13px",

          height: "13px",

          borderRadius: "50%",

          right: "-6px",

        }}

      />


      <div className="bg-gray-50 rounded-lg p-3 space-y-3">

        {/* Attribute Dropdown/Combobox */}

        <div>

          <label className="block text-sm font-medium text-gray-700 mb-1">

            Select Attribute

          </label>

          <Combobox

            options={attributes.map((attr) => ({

              value: attr.name,

              label: attr.name,

            }))}

            value={selectedAttribute}

            onValueChange={handleAttributeChange}

            placeholder="Choose or create attribute"

            createOption

          />

        </div>


        {/* Predefined Value Input */}

        <div>

          <label className="block text-sm font-medium text-gray-700 mb-1">

            Attribute Value

          </label>

          <input

            type="text"

            value={predefinedValue}

            onChange={(e) => handlePredefinedValueChange(e.target.value)}

            className="w-full p-2 border rounded-md text-sm"

            placeholder="Attribute value"

          />

        </div>

      </div>

    </div>

  );

};


const nodeTypes = {

  triggerNode: TriggerNode,

  textButtonNode: TextButtonNode,

  mediaNode: MediaNode,

  listNode: ListNode,

  attributeNode: AttributeNode,

};


const edgeTypes = {

  custom: CustomEdge,

};


const FlowHeader = ({ flowName, flowId, generateFlowData }) => {

  const router = useRouter();

  const [isSaving, setIsSaving] = useState(false);

  const [isPublishing, setIsPublishing] = useState(false);


  const validateNodes = (nodes) => {

    const errors = [];


    nodes.forEach((node) => {

      switch (node.type) {

        case "textButtonNode":

          // Message body validation

          if (!node.data.content?.trim()) {

            errors.push(

              `Message ${node.data.messageNumber}: Message body is required`

            );

          }


          // Buttons validation

          if (!node.data.buttons || node.data.buttons.length === 0) {

            errors.push(

              `Message ${node.data.messageNumber}: At least one button is required`

            );

          } else {

            // Check each button has label (text)

            node.data.buttons.forEach((button, index) => {

              if (!button.label?.trim()) {

                // Changed from title to label

                errors.push(

                  `Message ${node.data.messageNumber}: Button ${

                    index + 1

                  } must have text`

                );

              }

              // Validate button text length

              if (button.label?.length > 20) {

                errors.push(

                  `Message ${node.data.messageNumber}: Button ${

                    index + 1

                  } text cannot exceed 20 characters`

                );

              }

            });

            // Validate maximum buttons

            if (node.data.buttons.length > 3) {

              errors.push(

                `Message ${node.data.messageNumber}: Cannot have more than 3 buttons`

              );

            }

          }

          break;


        case "mediaNode":

          // Message body validation

          if (!node.data.content?.trim()) {

            errors.push(

              `Message ${node.data.messageNumber}: Message body is required`

            );

          }


          // Media validation

          if (!node.data.mediaUrl) {

            errors.push(

              `Message ${node.data.messageNumber}: Media file is required`

            );

          }


          // Buttons validation

          if (!node.data.buttons || node.data.buttons.length === 0) {

            errors.push(

              `Message ${node.data.messageNumber}: At least one button is required`

            );

          } else {

            // Check each button has label (text)

            node.data.buttons.forEach((button, index) => {

              if (!button.label?.trim()) {

                // Changed from title to label

                errors.push(

                  `Message ${node.data.messageNumber}: Button ${

                    index + 1

                  } must have text`

                );

              }

              // Validate button text length

              if (button.label?.length > 20) {

                errors.push(

                  `Message ${node.data.messageNumber}: Button ${

                    index + 1

                  } text cannot exceed 20 characters`

                );

              }

            });

            // Validate maximum buttons

            if (node.data.buttons.length > 3) {

              errors.push(

                `Message ${node.data.messageNumber}: Cannot have more than 3 buttons`

              );

            }

          }

          break;


        case "listNode":

          // Message body validation

          if (!node.data.content?.trim()) {

            errors.push(

              `Message ${node.data.messageNumber}: Message body is required`

            );

          }


          // List button text validation

          if (!node.data.buttonText?.trim()) {

            errors.push(

              `Message ${node.data.messageNumber}: List button text is required`

            );

          }

          // Validate button text length

          if (node.data.buttonText?.length > 20) {

            errors.push(

              `Message ${node.data.messageNumber}: List button text cannot exceed 20 characters`

            );

          }


          // Sections validation

          if (!node.data.sections || node.data.sections.length === 0) {

            errors.push(

              `Message ${node.data.messageNumber}: At least one section is required`

            );

          } else {

            // Validate each section

            node.data.sections.forEach((section, sectionIndex) => {

              // Section title validation

              if (!section.title?.trim()) {

                errors.push(

                  `Message ${node.data.messageNumber}: Section ${

                    sectionIndex + 1

                  } must have a title`

                );

              }

              // Validate section title length

              if (section.title?.length > 24) {

                errors.push(

                  `Message ${node.data.messageNumber}: Section ${

                    sectionIndex + 1

                  } title cannot exceed 24 characters`

                );

              }


              // Rows validation

              if (!section.rows || section.rows.length === 0) {

                errors.push(

                  `Message ${node.data.messageNumber}: Section ${

                    sectionIndex + 1

                  } must have at least one row`

                );

              } else {

                // Validate each row

                section.rows.forEach((row, rowIndex) => {

                  if (!row.title?.trim()) {

                    errors.push(

                      `Message ${node.data.messageNumber}: Row ${

                        rowIndex + 1

                      } in Section ${sectionIndex + 1} must have a title`

                    );

                  }

                  // Validate row title length

                  if (row.title?.length > 24) {

                    errors.push(

                      `Message ${node.data.messageNumber}: Row ${

                        rowIndex + 1

                      } in Section ${

                        sectionIndex + 1

                      } title cannot exceed 24 characters`

                    );

                  }

                  // Validate row description length if present

                  if (row.description && row.description.length > 72) {

                    errors.push(

                      `Message ${node.data.messageNumber}: Row ${

                        rowIndex + 1

                      } in Section ${

                        sectionIndex + 1

                      } description cannot exceed 72 characters`

                    );

                  }

                });

              }

            });


            // Validate section count

            if (node.data.sections.length > 10) {

              errors.push(

                `Message ${node.data.messageNumber}: Cannot have more than 10 sections`

              );

            }


            // Validate total row count across all sections

            const totalRows = node.data.sections.reduce(

              (total, section) => total + (section.rows?.length || 0),

              0

            );

            if (totalRows > 10) {

              errors.push(

                `Message ${node.data.messageNumber}: Total number of rows cannot exceed 10`

              );

            }

          }

          break;


        case "attributeNode":

          if (!node.data.attribute1?.trim()) {

            errors.push(`Attribute Node: Select Attribute is required`);

          }

          if (!node.data.attribute2?.trim()) {

            errors.push(`Attribute Node: Attribute Value is required`);

          }

          break;

      }

    });


    return errors;

  };


  const handlePublish = async () => {

    setIsSaving(true);

    try {

      setIsPublishing(true);


      // Call the function to get flow data

      const flowData = generateFlowData();


      console.log(flowData, "this is result");


      if (!flowData || !flowData.nodes || !flowData.edges) {

        toast.error("Invalid flow data");

        return;

      }


      // Basic validation

      if (!flowData.nodes.length) {

        toast.error("Flow must contain at least one node");

        return;

      }


      if (!flowData.edges.length) {

        toast.error("Flow must contain at least one connection");

        return;

      }


      // Validate nodes

      const validationErrors = validateNodes(flowData.nodes);

      if (validationErrors.length > 0) {

        // Show all errors in a list using toast


        toast(

          (t) => (

            <div>

              <h3 className="font-semibold mb-2">

                Please fix the following errors:

              </h3>

              <ul className="list-disc pl-4">

                {validationErrors.map((error, index) => (

                  <li key={index} className="text-sm">

                    {error}

                  </li>

                ))}

              </ul>

            </div>

          ),

          {

            duration: 4000, // Longer duration to read all errors

            style: {

              maxWidth: "500px",

              padding: "16px",

            },

          }

        );

        return;

      }


      const response = await axios.post(

        `${process.env.NEXT_PUBLIC_BASE_URL}/automation/${flowId}/save/`,

        {

          flowData: flowData,

        }

      );


      toast.success("Flow published successfully");

      setIsSaving(false);

    } catch (error) {

      setIsSaving(false);

      console.error("Error publishing flow:", error);

      toast.error("Failed to publish flow");

    } finally {

      setIsSaving(false);

      setIsPublishing(false);

    }

  };


  return (

    <div className="bg-white border-b border-gray-200">

      <div className="px-4 sm:px-6 lg:px-8 py-4">

        <div className="flex items-center justify-between">

          {/* Left side - Back arrow, Flow name and save status */}

          <div className="flex items-center space-x-4">

            <button

              onClick={() => router.push("/automations/whatsapp")}

              className="p-1 rounded-full hover:bg-gray-100 transition-colors duration-200"

              title="Go back"

            >

              <svg

                className="w-6 h-6 text-gray-600"

                fill="none"

                stroke="currentColor"

                viewBox="0 0 24 24"

              >

                <path

                  strokeLinecap="round"

                  strokeLinejoin="round"

                  strokeWidth={2}

                  d="M10 19l-7-7m0 0l7-7m-7 7h18"

                />

              </svg>

            </button>

            <div className="flex items-center space-x-4">

              <h1 className="text-xl font-semibold text-gray-900 flex items-center">

                {flowName}

              </h1>

              <span className="text-sm bg-gray-50 px-3 py-1 rounded-full border border-gray-200 text-gray-600 flex items-center">

                {isSaving ? (

                  <div className="flex items-center">

                    <svg

                      className="animate-spin h-4 w-4 mr-1.5 text-indigo-600"

                      viewBox="0 0 24 24"

                    >

                      <circle

                        className="opacity-25"

                        cx="12"

                        cy="12"

                        r="10"

                        stroke="currentColor"

                        strokeWidth="4"

                        fill="none"

                      />

                      <path

                        className="opacity-75"

                        fill="currentColor"

                        d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"

                      />

                    </svg>

                    Saving changes...

                  </div>

                ) : (

                  <div className="flex items-center">

                    <svg

                      className="h-4 w-4 mr-1.5 text-green-500"

                      fill="none"

                      strokeLinecap="round"

                      strokeLinejoin="round"

                      strokeWidth="2"

                      viewBox="0 0 24 24"

                      stroke="currentColor"

                    >

                      <path d="M5 13l4 4L19 7" />

                    </svg>

                    All changes saved

                  </div>

                )}

              </span>

            </div>

          </div>


          {/* Right side - Actions */}

          <div className="flex items-center space-x-4">

            {/* Test Button */}

            <button

              onClick={() => {}} // Add your test logic here

              className="inline-flex items-center px-4 py-2 border border-indigo-600 rounded-md text-sm font-medium text-indigo-700 bg-white hover:bg-indigo-50 hover:border-indigo-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 shadow-sm"

            >

              <svg

                xmlns="http://www.w3.org/2000/svg"

                className="h-4 w-4 mr-2"

                fill="none"

                viewBox="0 0 24 24"

                stroke="currentColor"

              >

                <path

                  strokeLinecap="round"

                  strokeLinejoin="round"

                  strokeWidth={2}

                  d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"

                />

                <path

                  strokeLinecap="round"

                  strokeLinejoin="round"

                  strokeWidth={2}

                  d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"

                />

              </svg>

              Test Flow

            </button>


            {/* Publish Button */}

            <button

              onClick={handlePublish}

              className={`inline-flex items-center px-4 py-2 border border-transparent rounded-md text-sm font-medium text-white shadow-sm transition-colors duration-200 ${

                isPublishing

                  ? "bg-indigo-500 cursor-not-allowed"

                  : "bg-indigo-600 hover:bg-indigo-700"

              } focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500`}

              disabled={isPublishing}

            >

              {isPublishing ? (

                <>

                  <svg

                    className="animate-spin -ml-1 mr-2 h-4 w-4 text-white"

                    xmlns="http://www.w3.org/2000/svg"

                    fill="none"

                    viewBox="0 0 24 24"

                  >

                    <circle

                      className="opacity-25"

                      cx="12"

                      cy="12"

                      r="10"

                      stroke="currentColor"

                      strokeWidth="4"

                    ></circle>

                    <path

                      className="opacity-75"

                      fill="currentColor"

                      d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"

                    ></path>

                  </svg>

                  Publishing...

                </>

              ) : (

                <>

                  <svg

                    className="h-4 w-4 mr-2"

                    fill="none"

                    stroke="currentColor"

                    viewBox="0 0 24 24"

                  >

                    <path

                      strokeLinecap="round"

                      strokeLinejoin="round"

                      strokeWidth={2}

                      d="M5 13l4 4L19 7"

                    />

                  </svg>

                  Publish Flow

                </>

              )}

            </button>

          </div>

        </div>

      </div>

    </div>

  );

};


// In your Flow component

const Flow = ({ setGenerateFlowData, initialFlowData, flowId }) => {

  // Add event handlers to the initial nodes


  const onEdgeDelete = useCallback((edgeId) => {

    setEdges((eds) => eds.filter((edge) => edge.id !== edgeId));

  }, []);


  // Initialize nodes

  const defaultTriggerNode = {

    id: "1",

    type: "triggerNode",

    position: { x: 400, y: 200 },

    data: {

      keywords: ["Hi"],

      onKeywordsUpdate: handleKeywordsUpdate,

    },

  };


  const [nodes, setNodes, onNodesChange] = useNodesState(

    initialFlowData?.nodes && initialFlowData.nodes.length > 0

      ? initialFlowData.nodes

      : [defaultTriggerNode]

  );


  // Initialize edges with the ref function

  const [edges, setEdges, onEdgesChange] = useEdgesState(

    initialFlowData?.edges?.map((edge) => ({

      id: edge.sourceHandle

        ? `edge-${edge.source}-${edge.sourceHandle}-${edge.target}`

        : `edge-${edge.source}-${edge.target}`,

      source: edge.source,

      sourceHandle: edge.sourceHandle,

      target: edge.target,

      type: "custom",

      data: {

        sourceButtonText: edge.data?.sourceButtonText,

        onDelete: onEdgeDelete,

      },

    })) || []

  );


  const [isDropdownVisible, setDropdownVisible] = useState(false);

  const [dropdownPosition, setDropdownPosition] = useState({ x: 0, y: 0 });

  const [pendingConnection, setPendingConnection] = useState(null);

  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });


  useEffect(() => {

    if (initialFlowData?.nodes) {

      setNodes((nodes) =>

        nodes.map((node) => ({

          ...node,

          data: {

            ...node.data,

            onKeywordsUpdate: handleKeywordsUpdate, // Add this line

            onButtonUpdate: handleButtonsUpdate,

            onContentUpdate: handleContentUpdate,

            onButtonsUpdate: handleButtonsUpdate,

            onMediaUpdate: handleMediaUpdate,

            onItemsUpdate: handleItemsUpdate,

            onAttributeUpdate: handleAttributeUpdate,

            onButtonTextUpdate: handleButtonTextUpdate,

            onSectionsUpdate: handleSectionsUpdate,

            onDelete: () => handleNodeDelete(node.id),

          },

        }))

      );

    }

  }, [initialFlowData]);


  // Handle node deletion

  const onNodesDelete = useCallback(

    (deleted) => {

      // Filter out the trigger node from deletion

      const nodesToDelete = deleted.filter(

        (node) => node.type !== "triggerNode"

      );


      // Remove associated edges

      const edgesToDelete = edges.filter((edge) =>

        nodesToDelete.find(

          (node) => node.id === edge.source || node.id === edge.target

        )

      );


      setEdges((eds) =>

        eds.filter((edge) => !edgesToDelete.find((del) => del.id === edge.id))

      );

    },

    [edges, setEdges]

  );


  function handleButtonsUpdate(nodeId, newButtons) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              buttons: newButtons,

            },

          };

        }

        return node;

      })

    );

  }


  function handleContentUpdate(nodeId, newContent) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              content: newContent,

            },

          };

        }

        return node;

      })

    );

  }


  function handleMediaUpdate(

    nodeId,

    mediaUrl,

    mediaType,

    uploadedId,

    fileName

  ) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              mediaUrl,

              mediaType,

              uploadedId: uploadedId, // Make sure this is set

              fileName: fileName, // Make sure this is set

            },

          };

        }

        return node;

      })

    );

  }


  function handleItemsUpdate(nodeId, newItems) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              items: newItems,

            },

          };

        }

        return node;

      })

    );

  }


  function handleAttributeUpdate(nodeId, field, value) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              [field]: value,

            },

          };

        }

        return node;

      })

    );

  }


  function handleKeywordsUpdate(nodeId, newKeywords) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              keywords: newKeywords,

              onKeywordsUpdate: handleKeywordsUpdate,

            },

          };

        }

        return node;

      })

    );

  }


  function handleButtonTextUpdate(nodeId, newButtonText) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              buttonText: newButtonText,

            },

          };

        }

        return node;

      })

    );

  }


  function handleSectionsUpdate(nodeId, newSections) {

    setNodes((nds) =>

      nds.map((node) => {

        if (node.id === nodeId) {

          return {

            ...node,

            data: {

              ...node.data,

              sections: newSections,

            },

          };

        }

        return node;

      })

    );

  }


  const onConnectStart = useCallback((_, { nodeId, handleId }) => {

    setPendingConnection({ sourceId: nodeId, sourceHandleId: handleId });

    setDropdownVisible(false);

  }, []);


  const onConnectEnd = useCallback((event) => {

    const targetNode = event.target.closest(".react-flow__node");


    if (!targetNode) {

      const bounds = document

        .querySelector(".react-flow")

        .getBoundingClientRect();


      // Calculate position relative to the viewport

      const position = {

        x: event.clientX - bounds.left,

        y: event.clientY - bounds.top,

      };


      setDropdownPosition(position);

      setMousePosition(position);

      setDropdownVisible(true);

    }

  }, []);


  /// Modify the onConnect function to ensure onDelete is passed

  const onConnect = useCallback(

    (params) => {

      const sourceNode = nodes.find((node) => node.id === params.source);

      const sourceButtonText = sourceNode?.data?.buttons?.find(

        (btn) => btn.handleId === params.sourceHandle

      )?.label;


      const newEdge = {

        ...params,

        id: params.sourceHandle

          ? `edge-${params.source}-${params.sourceHandle}-${params.target}`

          : `edge-${params.source}-${params.target}`,

        type: "custom",

        data: {

          onDelete: onEdgeDelete,

          sourceButtonText: sourceButtonText,

        },

      };


      setEdges((eds) => addEdge(newEdge, eds));

    },

    [nodes, onEdgeDelete]

  );

  const isConnectionAllowed = useCallback(() => {

    return true; // Allow all connections

  }, []);


  // Add this state to track the flow viewport

  const [viewport, setViewport] = useState({ x: 0, y: 0, zoom: 1 });


  // Update viewport state when it changes

  const onViewportChange = useCallback((viewport) => {

    setViewport(viewport);

  }, []);


  const handleDropdownSelect = useCallback(

    (option) => {

      if (!pendingConnection) return;


      const newNodeId = `node-${Date.now()}`;

      const messageNodes = nodes.filter((node) =>

        ["textButtonNode", "mediaNode", "listNode"].includes(node.type)

      );

      const messageNumber = messageNodes.length + 1;


      // Calculate the actual position considering viewport transform

      const position = {

        x: (dropdownPosition.x - viewport.x) / viewport.zoom,

        y: (dropdownPosition.y - viewport.y) / viewport.zoom,

      };


      let nodeType;

      switch (option) {

        case "Text + Button":

          nodeType = "textButtonNode";

          break;

        case "Media":

          nodeType = "mediaNode";

          break;

        case "List":

          nodeType = "listNode";

          break;

        case "Attribute":

          nodeType = "attributeNode";

          break;

        default:

          nodeType = "textButtonNode";

      }


      // Create new node with corrected position

      const newNode = {

        id: newNodeId,

        type: nodeType,

        position: position, // Use the corrected position

        data: {

          type: option,

          content: "",

          buttons: [],

          messageNumber: nodeType !== "attributeNode" ? messageNumber : null,

          onButtonUpdate: handleButtonsUpdate,

          onContentUpdate: handleContentUpdate,

          onButtonsUpdate: handleButtonsUpdate,

          onMediaUpdate: handleMediaUpdate,

          onItemsUpdate: handleItemsUpdate,

          onAttributeUpdate: handleAttributeUpdate,

          onButtonTextUpdate: handleButtonTextUpdate,

          onSectionsUpdate: handleSectionsUpdate,

          onDelete: () => handleNodeDelete(newNodeId),

        },

      };


      // Create new edge if there's a pending connection

      if (pendingConnection.sourceId) {

        const newEdge = {

          id: pendingConnection.sourceHandleId

            ? `edge-${pendingConnection.sourceId}-${pendingConnection.sourceHandleId}-${newNodeId}`

            : `edge-${pendingConnection.sourceId}-${newNodeId}`,

          source: pendingConnection.sourceId,

          sourceHandle: pendingConnection.sourceHandleId,

          target: newNodeId,

          type: "custom",

          data: {

            onDelete: onEdgeDelete,

          },

        };


        setEdges((eds) => [...eds, newEdge]);

      }


      setNodes((nds) => [...nds, newNode]);

      setDropdownVisible(false);

      setPendingConnection(null);

    },

    [pendingConnection, dropdownPosition, viewport, nodes, setNodes, setEdges]

  );


  // Handle node deletion

  const handleNodeDelete = useCallback(

    (nodeId) => {

      setNodes((nds) => {

        const filteredNodes = nds.filter((node) => node.id !== nodeId);

        // Recalculate message numbers

        return filteredNodes.map((node) => {

          if (["textButtonNode", "mediaNode", "listNode"].includes(node.type)) {

            const newMessageNumber =

              filteredNodes.filter(

                (n) =>

                  ["textButtonNode", "mediaNode", "listNode"].includes(

                    n.type

                  ) && n.id < node.id

              ).length + 1;

            return {

              ...node,

              data: {

                ...node.data,

                messageNumber: newMessageNumber,

              },

            };

          }

          return node;

        });

      });


      setEdges((eds) =>

        eds.filter((edge) => edge.source !== nodeId && edge.target !== nodeId)

      );

    },

    [setNodes, setEdges]

  );


  const generateFlowData = useCallback(() => {

    const mappedNodes = nodes.map((node) => {

      const baseNodeData = {

        id: node.id,

        type: node.type,

        position: node.position,

      };


      switch (node.type) {

        case "triggerNode":

          return {

            ...baseNodeData,

            data: {

              keywords: node.data.keywords || [],

            },

          };


        case "textButtonNode":

          return {

            ...baseNodeData,

            data: {

              content: node.data.content || "",

              buttons:

                node.data.buttons?.map((button) => ({

                  label: button.label,

                  handleId: button.handleId,

                })) || [],

              messageNumber: node.data.messageNumber,

            },

          };


        case "mediaNode":

          return {

            ...baseNodeData,

            data: {

              content: node.data.content || "",

              mediaUrl: node.data.mediaUrl || "",

              mediaType: node.data.mediaType || "",

              uploadedId: node.data.uploadedId || "",

              fileName: node.data.fileName || "",

              buttons:

                node.data.buttons?.map((button) => ({

                  label: button.label,

                  handleId: button.handleId,

                })) || [],

              messageNumber: node.data.messageNumber,

            },

          };


        case "listNode":

          return {

            ...baseNodeData,

            data: {

              content: node.data.content || "",

              buttonText: node.data.buttonText || "Options",

              sections:

                node.data.sections?.map((section) => ({

                  title: section.title,

                  rows: section.rows.map((row) => ({

                    id: row.id,

                    title: row.title,

                    description: row.description,

                  })),

                })) || [],

              messageNumber: node.data.messageNumber,

            },

          };


        case "attributeNode":

          return {

            ...baseNodeData,

            data: {

              attribute1: node.data.attribute1 || "",

              attribute2: node.data.attribute2 || "",

            },

          };


        default:

          return baseNodeData;

      }

    });


    // Modify how edges are mapped to include all necessary data

    const mappedEdges = edges.map((edge) => ({

      id: edge.sourceHandle

        ? `edge-${edge.source}-${edge.sourceHandle}-${edge.target}`

        : `edge-${edge.source}-${edge.target}`,

      source: edge.source,

      sourceHandle: edge.sourceHandle,

      target: edge.target,

      type: edge.type,

      data: {

        sourceButtonText: edge.data?.sourceButtonText,

      },

    }));


    return {

      nodes: mappedNodes,

      edges: mappedEdges,

    };

  }, [nodes, edges]);


  // Update the parent component with the generation function

  useEffect(() => {

    setGenerateFlowData(() => generateFlowData);

  }, [generateFlowData, setGenerateFlowData]);


  const [reactFlowInstance, setReactFlowInstance] = useState(null);


  // Function to handle quick navigation

  const handleQuickNav = (direction) => {

    if (!reactFlowInstance) return;


    const nodes = reactFlowInstance.getNodes();

    if (!nodes.length) return;


    // Calculate bounds

    let targetY;

    if (direction === "top") {

      targetY = Math.min(...nodes.map((node) => node.position.y)) - 100;

    } else {

      targetY = Math.max(...nodes.map((node) => node.position.y)) + 100;

    }


    // Smooth scroll to position

    reactFlowInstance.setViewport(

      {

        x: reactFlowInstance.getViewport().x,

        y: -targetY,

        zoom: reactFlowInstance.getViewport().zoom,

      },

      { duration: 800 }

    );

  };


  // Add new state for tracking save status

  const [lastSaved, setLastSaved] = useState(null);

  const [saveStatus, setSaveStatus] = useState("All changes saved");


  // Function to format current date-time in UTC

  const formatDateTime = () => {

    const now = new Date();

    return now.toLocaleTimeString("en-IN", {

      hour: "2-digit",

      minute: "2-digit",

      second: "2-digit",

      hour12: true,

    });

  };

  // Function to save flow data

  const saveFlowData = useCallback(async () => {

    try {

      const flowData = generateFlowData();


      const response = await axios.post(

        `${process.env.NEXT_PUBLIC_BASE_URL}/automation/${flowId}/save/`,

        {

          flowData: flowData,

        }

      );


      if (response.status !== 200) {

        throw new Error("Failed to save");

      }


      setLastSaved(formatDateTime());

      setSaveStatus("All changes saved");


      console.log("all change saved");


      // Optional: Show success toast

      // toast.success('Flow autosaved successfully');

    } catch (error) {

      console.error("Autosave failed:", error);

      setSaveStatus("Failed to save");

      toast.error("Failed to autosave flow");

    }

  }, [generateFlowData, flowId]);


  // Set up autosave interval

  useEffect(() => {

    if (!flowId) return; // Don't set up autosave if no flowId


    // Save every 5 minutes

    const autoSaveInterval = setInterval(() => {

      setSaveStatus("Saving...");

      saveFlowData();

    }, 5 * 60 * 1000); // 5 minutes


    // Save when user leaves the page

    // const handleBeforeUnload = (e) => {

    //   saveFlowData();

    //   e.preventDefault();

    //   e.returnValue =

    //     "You have unsaved changes. Are you sure you want to leave?";

    // };


    // window.addEventListener("beforeunload", handleBeforeUnload);


    // Cleanup

    return () => {

      clearInterval(autoSaveInterval);

      // window.removeEventListener("beforeunload", handleBeforeUnload);

    };

  }, [saveFlowData, flowId]);


  return (

    <div className="relative w-full h-[calc(100vh-64px)] overflow-hidden">

      <ReactFlow

        nodes={nodes}

        edges={edges}

        onNodesChange={onNodesChange}

        onEdgesChange={onEdgesChange}

        onConnectStart={onConnectStart}

        onConnectEnd={onConnectEnd}

        onConnect={onConnect}

        nodeTypes={nodeTypes}

        edgeTypes={edgeTypes}

        defaultEdgeOptions={{

          type: "custom",

          data: { onDelete: onEdgeDelete },

        }}

        connectionMode="loose"

        fitView

        onInit={setReactFlowInstance}

        onViewportChange={onViewportChange}

      >

        <Controls

          position="bottom-left"

          style={{ position: "fixed", left: 20, bottom: 20 }}

          showInteractive={false}

        />

        <MiniMap

          position="bottom-right"

          style={{ position: "fixed", right: 20, bottom: 100 }}

          zoomable

          pannable

        />

        <Background gap={12} size={1} />

      </ReactFlow>


      <div className="absolute right-8 top-4 flex items-center gap-2 bg-white px-4 py-2 rounded-md shadow-sm border border-gray-200 z-[1000]">

        <div className="flex items-center gap-2">

          {saveStatus === "Saving..." ? (

            <svg

              className="animate-spin h-4 w-4 text-gray-500"

              xmlns="http://www.w3.org/2000/svg"

              fill="none"

              viewBox="0 0 24 24"

            >

              <circle

                className="opacity-25"

                cx="12"

                cy="12"

                r="10"

                stroke="currentColor"

                strokeWidth="4"

              ></circle>

              <path

                className="opacity-75"

                fill="currentColor"

                d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"

              ></path>

            </svg>

          ) : saveStatus === "Failed to save" ? (

            <svg

              className="h-4 w-4 text-red-500"

              fill="none"

              stroke="currentColor"

              viewBox="0 0 24 24"

            >

              <path

                strokeLinecap="round"

                strokeLinejoin="round"

                strokeWidth="2"

                d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"

              ></path>

            </svg>

          ) : (

            <svg

              className="h-4 w-4 text-green-500"

              fill="none"

              stroke="currentColor"

              viewBox="0 0 24 24"

            >

              <path

                strokeLinecap="round"

                strokeLinejoin="round"

                strokeWidth="2"

                d="M5 13l4 4L19 7"

              ></path>

            </svg>

          )}

          <span className="text-sm text-gray-600">{saveStatus}</span>

        </div>

        {lastSaved && (

          <span className="text-xs text-gray-400">Last saved: {lastSaved}</span>

        )}

      </div>


      {/* Quick Navigation Controls */}

      {/* <div className="absolute right-8 top-1/2 transform -translate-y-1/2 flex flex-col gap-4 z-[1000]">

        <button

          onClick={() => handleQuickNav("top")}

          className="p-3 bg-white rounded-full shadow-lg hover:bg-gray-50 transition-colors duration-200 group"

          title="Scroll to top"

        >

          <svg

            className="w-5 h-5 text-gray-600 group-hover:text-indigo-600"

            fill="none"

            viewBox="0 0 24 24"

            stroke="currentColor"

          >

            <path

              strokeLinecap="round"

              strokeLinejoin="round"

              strokeWidth={2}

              d="M5 15l7-7 7 7"

            />

          </svg>

        </button>


        <button

          onClick={() => handleQuickNav("bottom")}

          className="p-3 bg-white rounded-full shadow-lg hover:bg-gray-50 transition-colors duration-200 group"

          title="Scroll to bottom"

        >

          <svg

            className="w-5 h-5 text-gray-600 group-hover:text-indigo-600"

            fill="none"

            viewBox="0 0 24 24"

            stroke="currentColor"

          >

            <path

              strokeLinecap="round"

              strokeLinejoin="round"

              strokeWidth={2}

              d="M19 9l-7 7-7-7"

            />

          </svg>

        </button>

      </div> */}


      {isDropdownVisible && (

        <div className="z-[1000]">

          <NodeTypeDropdown

            options={["Text + Button", "Media", "List", "Attribute"]}

            onSelect={handleDropdownSelect}

            onCancel={() => {

              setDropdownVisible(false);

              setPendingConnection(null);

            }}

            position={dropdownPosition}

          />

        </div>

      )}

    </div>

  );

};


// Add this function to handle publishing


export default function WhatsAppFlow() {

  const router = useRouter();

  const { flowId } = router.query;

  const [flowName, setFlowName] = useState("");

  const [generateFlowData, setGenerateFlowData] = useState(() => () => ({}));

  const [initialFlowData, setInitialFlowData] = useState(null);


  useEffect(() => {

    if (flowId) {

      const fetchFlowDetails = async () => {

        try {

          const response = await axios.get(

            `${process.env.NEXT_PUBLIC_BASE_URL}/automation/${flowId}/`

          );

          setFlowName(response.data.name);

          // If there's no flowData or it's empty, create default structure

          const flowData = response.data.flowData || {

            nodes: [],

            edges: [],

          };

          setInitialFlowData(flowData);

          console.log(response.data.flowData, "!!!!!!!!!!");

        } catch (error) {

          // If it fails to fetch (new flow), initialize with empty data

          setInitialFlowData({

            nodes: [],

            edges: [],

          });

          toast.error("Failed to load flow details");

          console.error("Error fetching flow details:", error);

        }

      };


      fetchFlowDetails();

    }

  }, [flowId]);


  if (!flowId || !initialFlowData) return null;


  return (

    <Layout>

      <FlowHeader

        flowName={flowName}

        flowId={flowId}

        generateFlowData={generateFlowData}

      />

      <ReactFlowProvider>

        <Flow

          setGenerateFlowData={setGenerateFlowData}

          initialFlowData={initialFlowData}

          flowId={flowId}

        />

      </ReactFlowProvider>

      <Toaster position="top-center" />

    </Layout>

  );

}


Comments

Popular posts from this blog

REACT NATIVE STYLE TRICKS

Tips for Django

chat model