2025-11-12 11:41:38 +01:00

272 lines
9.5 KiB
TypeScript

"use client"
import { useState, useEffect, useCallback } from "react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
import {
Send,
Paperclip,
Smile,
MoreHorizontal,
Folder,
MessageSquare
} from "lucide-react"
import MessageBus from "@/lib/4nk/MessageBus"
import { iframeUrl } from "@/app/page"
import { FolderChatData } from "@/lib/4nk/models/FolderData"
import { use4NK, EnrichedFolderData } from "@/lib/contexts/FourNKContext"
// Interface pour les props (accepte null)
interface FolderChatProps {
folder: EnrichedFolderData | null;
}
export default function FolderChat({ folder }: FolderChatProps) {
const [newMessage, setNewMessage] = useState("")
const [activeTab, setActiveTab] = useState<'owner' | 'general'>('owner');
const [ownerMessages, setOwnerMessages] = useState<FolderChatData[]>([]);
const [generalMessages, setGeneralMessages] = useState<FolderChatData[]>([]);
const {
userPairingId,
setFolderProcesses,
setFolderPrivateData,
} = use4NK();
useEffect(() => {
setOwnerMessages(folder?.messages_owner || []);
setGeneralMessages(folder?.messages || []);
}, [folder]);
// Filtre les messages basé sur l'onglet actif
const filteredMessages = activeTab === 'owner' ? ownerMessages : generalMessages;
const handleProcessUpdate = useCallback(async (processId: string, key: string, value: any) => {
// Note : 'value' est l'objet newMessageData que vous avez passé
try {
const messageBus = MessageBus.getInstance(iframeUrl);
await messageBus.isReady();
const updateData = {
[key]: value
};
// 1. Mettre à jour le process
const updatedProcess = await messageBus.updateProcess(processId, updateData, [], null);
console.log("Process mis à jour :", updatedProcess);
if (!updatedProcess) {
throw new Error('updateProcess n\'a pas retourné de process mis à jour');
}
// 2. Extraire le newStateId
const newStateId = updatedProcess.diffs[0]?.state_id;
if (!newStateId) {
throw new Error('No new state id found');
}
// 3. Notifier et Valider
await messageBus.notifyProcessUpdate(processId, newStateId);
await messageBus.validateState(processId, newStateId);
// 4. Mettre à jour l'objet process dans le contexte
setFolderProcesses((prevProcesses: any) => ({
...prevProcesses,
[processId]: updatedProcess.current_process
}));
// 5. Mettre à jour le cache des données privées (CORRIGÉ)
// D'abord, convertir l'objet 'value' en Map, comme l'attend loadFoldersFrom4NK
const valueAsMap = new Map(Object.entries(value));
// Ensuite, créer l'objet conteneur structuré
const privateDataForCache = {
[key]: valueAsMap
};
// Enfin, stocker cet objet structuré dans le cache
setFolderPrivateData((prevData) => ({
...prevData,
[newStateId]: privateDataForCache // <-- Utiliser l'objet formaté
}));
console.log('Process & cache de données privées mis à jour avec succès.');
} catch (error) {
console.error('Error updating field:', error);
}
}, [setFolderProcesses, setFolderPrivateData]);
const handleSendMessage = useCallback(() => {
if (newMessage.trim() && folder) {
console.log(`Envoi message [${activeTab}] dans le dossier:`, folder?.name, "Msg:", newMessage)
const key = activeTab === 'owner' ? 'messages_owner' : 'messages'
const newMessageData: FolderChatData = {
timestamp: Date.now(),
sender: (userPairingId ? userPairingId : ''),
receiver: '',
fromRole: 'owner',
toRole: 'owner',
message: newMessage,
}
if (activeTab === 'owner') {
setOwnerMessages(prevMessages => [...prevMessages, newMessageData]);
} else {
setGeneralMessages(prevMessages => [...prevMessages, newMessageData]);
}
// Appelle la fonction mémorisée
handleProcessUpdate(folder.processId, key, newMessageData)
setNewMessage("")
}
}, [
// La fonction doit être recréée si une de ces valeurs change :
newMessage,
folder,
activeTab,
userPairingId,
handleProcessUpdate, // <-- Mettez la fonction mémorisée ici
setOwnerMessages,
setGeneralMessages,
setNewMessage
]);
// Si aucun dossier n'est sélectionné, afficher un placeholder
if (!folder) {
return (
<div className="flex h-full items-center justify-center bg-gray-800 text-gray-500 p-6">
<div className="text-center">
<MessageSquare className="h-12 w-12 mx-auto mb-4" />
<h3 className="text-lg font-medium text-gray-100 mb-2">
Chat de dossier
</h3>
<p className="text-gray-400">
Sélectionnez un dossier pour voir la conversation
</p>
</div>
</div>
)
}
// Si un dossier EST sélectionné, afficher le chat complet
return (
<div className="flex flex-col h-full bg-gray-800 text-gray-100">
{/* En-tête du chat */}
<div className="p-4 border-b border-gray-700">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-green-800 rounded-full flex items-center justify-center flex-shrink-0">
<Folder className="h-5 w-5 text-green-400" />
</div>
<div>
<h3 className="font-medium text-gray-100">
{folder.name}
</h3>
{/* ID du dossier supprimé */}
</div>
</div>
<Button variant="ghost" size="sm" className="text-gray-400 hover:text-gray-100 hover:bg-gray-700">
<MoreHorizontal className="h-4 w-4" />
</Button>
</div>
</div>
{/* Onglets "Owner" / "General" */}
<div className="p-2 flex border-b border-gray-700 bg-gray-900">
<Button
variant={activeTab === 'owner' ? "secondary" : "ghost"}
size="sm"
onClick={() => setActiveTab('owner')}
className={`flex-1 ${activeTab === 'owner' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'}`}
>
Propriétaires
</Button>
<Button
variant={activeTab === 'general' ? "secondary" : "ghost"}
size="sm"
onClick={() => setActiveTab('general')}
className={`flex-1 ${activeTab === 'general' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'}`}
>
Général
</Button>
</div>
{/* Zone des messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4 bg-gray-900">
{filteredMessages.length > 0 ? filteredMessages.map((msg, i) => (
<div
key={i}
className={`flex items-start gap-3 ${msg.sender === userPairingId ? 'justify-end' : ''}`}
>
{msg.sender != userPairingId && (
<div className="w-8 h-8 bg-blue-800 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-xs text-blue-300 font-medium">{msg.sender.slice(0, 2)}</span>
</div>
)}
<div>
<div
className={`p-3 rounded-lg ${msg.sender === 'me'
? 'bg-blue-600 text-white rounded-br-none'
: 'bg-gray-700 text-gray-100 rounded-bl-none'
}`}
>
{msg.sender != userPairingId && (
<p className="text-xs font-medium text-blue-300 mb-1">KAAK</p>
)}
<p>{msg.message}</p>
</div>
<p className={`text-xs text-gray-500 mt-1 ${msg.sender === userPairingId ? 'text-right' : ''}`}>
{new Date(Number(msg.timestamp)).toLocaleTimeString('fr-FR', {
hour: '2-digit',
minute: '2-digit'
})}
</p>
</div>
</div>
)) : (
<div className="flex h-full items-center justify-center text-center text-gray-500 p-4">
<p>Aucun message dans le chat "{activeTab}"</p>
</div>
)}
</div>
{/* Input de message */}
<div className="p-4 border-t border-gray-700">
<div className="flex items-end space-x-2">
<Button variant="ghost" size="sm" className="text-gray-400 hover:text-gray-100 hover:bg-gray-700">
<Paperclip className="h-5 w-5" />
</Button>
<Button variant="ghost" size="sm" className="text-gray-400 hover:text-gray-100 hover:bg-gray-700">
<Smile className="h-5 w-5" />
</Button>
<Textarea
placeholder={`Message (${activeTab})...`}
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
}}
rows={1}
className="resize-none flex-1 bg-gray-700 border-gray-700 text-gray-100 placeholder-gray-400 focus:border-blue-500 focus:ring-0"
/>
<Button
onClick={handleSendMessage}
disabled={!newMessage.trim()}
className="bg-blue-600 hover:bg-blue-700 text-white"
>
<Send className="h-4 w-4" />
</Button>
</div>
</div>
</div>
)
}