theo-agent-dashboard

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

TopicItem.tsx (3546B)


      1 import { useState, useRef, useEffect } from "react";
      2 import type { Topic } from "../../store/topic";
      3 import { useTopicStore } from "../../store/topic";
      4 
      5 interface TopicItemProps {
      6   topic: Topic;
      7   isSelected: boolean;
      8   onSelect: () => void;
      9 }
     10 
     11 export function TopicItem({ topic, isSelected, onSelect }: TopicItemProps) {
     12   const [menuOpen, setMenuOpen] = useState(false);
     13   const [editing, setEditing] = useState(false);
     14   const [editName, setEditName] = useState(topic.name);
     15   const menuRef = useRef<HTMLDivElement>(null);
     16   const { updateTopic, deleteTopic } = useTopicStore();
     17 
     18   useEffect(() => {
     19     if (!menuOpen) return;
     20     const handleClick = (e: MouseEvent) => {
     21       if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
     22         setMenuOpen(false);
     23       }
     24     };
     25     document.addEventListener("mousedown", handleClick);
     26     return () => document.removeEventListener("mousedown", handleClick);
     27   }, [menuOpen]);
     28 
     29   const handleRename = async () => {
     30     if (editName.trim() && editName !== topic.name) {
     31       await updateTopic(topic.id, { name: editName.trim() });
     32     }
     33     setEditing(false);
     34   };
     35 
     36   const handleRecolor = async (color: string) => {
     37     await updateTopic(topic.id, { color });
     38     setMenuOpen(false);
     39   };
     40 
     41   const handleDelete = async () => {
     42     await deleteTopic(topic.id);
     43     setMenuOpen(false);
     44   };
     45 
     46   return (
     47     <div
     48       className={`group flex items-center gap-2 rounded px-2 py-1.5 text-sm cursor-pointer transition-colors ${
     49         isSelected
     50           ? "bg-gray-700 text-white"
     51           : "text-gray-400 hover:bg-gray-800 hover:text-gray-200"
     52       }`}
     53       onClick={onSelect}
     54       onContextMenu={(e) => {
     55         e.preventDefault();
     56         setMenuOpen(true);
     57       }}
     58     >
     59       <span
     60         className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
     61         style={{ backgroundColor: topic.color }}
     62       />
     63       {editing ? (
     64         <input
     65           autoFocus
     66           value={editName}
     67           onChange={(e) => setEditName(e.target.value)}
     68           onBlur={handleRename}
     69           onKeyDown={(e) => e.key === "Enter" && handleRename()}
     70           className="flex-1 bg-transparent border-b border-gray-600 outline-none text-sm"
     71           onClick={(e) => e.stopPropagation()}
     72         />
     73       ) : (
     74         <span className="flex-1 truncate">{topic.name}</span>
     75       )}
     76       <span className="text-xs text-gray-600">{topic.session_count ?? 0}</span>
     77 
     78       {menuOpen && (
     79         <div
     80           ref={menuRef}
     81           className="absolute right-0 z-50 mt-1 w-40 rounded border border-gray-700 bg-gray-900 shadow-lg"
     82         >
     83           <button
     84             className="w-full px-3 py-1.5 text-left text-sm text-gray-300 hover:bg-gray-800"
     85             onClick={() => {
     86               setEditing(true);
     87               setMenuOpen(false);
     88             }}
     89           >
     90             Rename
     91           </button>
     92           <div className="flex gap-1 px-3 py-1.5">
     93             {["#6366f1", "#ef4444", "#10b981", "#f59e0b", "#8b5cf6"].map(
     94               (c) => (
     95                 <button
     96                   key={c}
     97                   className="h-4 w-4 rounded-full border border-gray-600"
     98                   style={{ backgroundColor: c }}
     99                   onClick={() => handleRecolor(c)}
    100                 />
    101               ),
    102             )}
    103           </div>
    104           <button
    105             className="w-full px-3 py-1.5 text-left text-sm text-red-400 hover:bg-gray-800"
    106             onClick={handleDelete}
    107           >
    108             Delete
    109           </button>
    110         </div>
    111       )}
    112     </div>
    113   );
    114 }