diff --git a/src/components/common/TagInput.jsx b/src/components/common/TagInput.jsx index 58f3d16f..cdc88afb 100644 --- a/src/components/common/TagInput.jsx +++ b/src/components/common/TagInput.jsx @@ -1,32 +1,89 @@ -import React, { useState, useEffect } from "react"; import { useFormContext } from "react-hook-form"; +import React, { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import { changeMaster } from "../../slices/localVariablesSlice"; const TagInput = ({ label = "Tags", name = "tags", - placeholder = "Enter ... ", + placeholder = "Start typing to add...", color = "#e9ecef", + options = [], }) => { const [tags, setTags] = useState([]); const [input, setInput] = useState(""); - const { setValue } = useFormContext(); + const [suggestions, setSuggestions] = useState([]); + const { setValue, trigger } = useFormContext(); + const dispatch = useDispatch(); useEffect(() => { - setValue(name, tags); // sync to form when tags change - }, [tags, name, setValue]); + setValue( + name, + tags.map((tag) => ({ + id: tag.id ?? null, + name: tag.name, + })) + ); + }, [ tags, name, setValue ] ); - const addTag = (e) => { - e.preventDefault(); - const trimmed = input.trim(); - if (trimmed !== "" && !tags.includes(trimmed)) { - setTags([...tags, trimmed]); - setInput(""); + useEffect(() => { + if (input.trim() === "") { + setSuggestions([]); + } else { + const filtered = options?.filter( + (opt) => + opt?.name?.toLowerCase()?.includes(input?.toLowerCase()) && + !tags?.some((tag) => tag?.name === opt?.name) + ); + setSuggestions(filtered); + } + }, [input, options, tags]); + + const addTag = async ( tagObj ) => + { + if (!tags.some((tag) => tag.id === tagObj.id)) { + const cleanedTag = { + id: tagObj.id ?? null, + name: tagObj.name, + }; + const newTags = [...tags, cleanedTag]; + setTags(newTags); + setValue(name, newTags, { shouldValidate: true }); // ✅ only id + name + await trigger(name); + setInput(""); + setSuggestions([]); + } +}; + + + const handleInputKeyDown = (e) => { + if (e.key === "Enter" && input.trim() !== "") { + e.preventDefault(); + const existing = options.find( + (opt) => opt.name.toLowerCase() === input.trim().toLowerCase() + ); + const newTag = existing + ? existing + : { + id: null, + name: input.trim(), + description: input.trim(), + }; + addTag(newTag); // Call async function (not awaiting because it's UI input) + } else if (e.key === "Backspace" && input === "") { + setTags((prev) => prev.slice(0, -1)); } }; - const removeTag = (removeIndex) => { - const updated = tags.filter((_, i) => i !== removeIndex); - setTags(updated); + const handleSuggestionClick = (suggestion) => { + addTag(suggestion); + }; + + const removeTag = (indexToRemove) => { + const newTags = tags.filter((_, i) => i !== indexToRemove); + setTags(newTags); + setValue(name, newTags, { shouldValidate: true }); + trigger(name); }; const backgroundColor = color || "#f8f9fa"; @@ -34,32 +91,72 @@ const TagInput = ({ return ( <> - -
- {tags.map((tag, i) => ( - + {label} + + +
+
+ {tags.map((tag, index) => ( + + {tag.name} + removeTag(index)} + style={{ cursor: "pointer" }} + /> + + ))} + setInput(e.target.value)} + onKeyDown={handleInputKeyDown} + placeholder={placeholder} style={{ - color: iconColor, - backgroundColor, - padding: "2px 3px", - borderRadius: "2px" + border: "none", + outline: "none", + flex: 1, + minWidth: "120px", + }} + onFocus={() => dispatch(changeMaster("Contact Tag"))} + /> +
+ + {suggestions.length > 0 && ( +
    - {tag} - removeTag(i)}> - - ))} - setInput(e.target.value)} - onKeyDown={(e) => (e.key === "Enter" ? addTag(e) : null)} - placeholder={placeholder} - style={{ outline: "none", minWidth: "120px" }} - /> + {suggestions.map((sugg, i) => ( +
  • handleSuggestionClick(sugg)} + style={{ cursor: "pointer", fontSize: "0.875rem" }} + > + {sugg.name} +
  • + ))} +
+ )}
);