diff --git a/components/4nk/Chat.tsx b/components/4nk/Chat.tsx index a61e344..f1c71f8 100644 --- a/components/4nk/Chat.tsx +++ b/components/4nk/Chat.tsx @@ -28,20 +28,54 @@ import { Zap, } from "lucide-react" import { useSearchParams } from "next/navigation" +import { PairingProcess } from "@/lib/4nk/models/PairingProcess" interface ChatProps { heightClass?: string + processes?: any + myProcesses?: string[] } -export default function Chat({ heightClass = "h-[calc(100vh-8rem)]" }: ChatProps) { - const [selectedConversation, setSelectedConversation] = useState("global") +export default function Chat({ heightClass = "h-[calc(100vh-8rem)]", processes, myProcesses }: ChatProps) { + const [selectedConversation, setSelectedConversation] = useState("") const [newMessage, setNewMessage] = useState("") + const [pairingProcesses, setPairingProcesses] = useState([]) + const [isLoading, setIsLoading] = useState(true) const searchParams = useSearchParams() const userId = searchParams.get("user") const messageType = searchParams.get("message") const groupType = searchParams.get("type") + // Filter pairing processes when processes prop changes + useEffect(() => { + if (processes && Object.keys(processes).length > 0) { + setIsLoading(true) + + // Filter pairing processes (those with memberPublicName in publicData) + const pairingList: PairingProcess[] = [] + Object.entries(processes).forEach(([processId, process]) => { + // Get the latest state + const latestState = process.states?.[process.states.length - 2] // -2 because last state is usually empty + + // Check if memberPublicName field exists (even if empty) - indicates pairing process + if (latestState?.public_data?.hasOwnProperty('memberPublicName')) { + const memberPublicName = latestState.public_data.memberPublicName || `Pairing ${processId.slice(0, 8)}` + pairingList.push({ + id: processId, + memberPublicName: memberPublicName + }) + } + }) + + setPairingProcesses(pairingList) + setIsLoading(false) + } else { + setIsLoading(true) + setPairingProcesses([]) + } + }, [processes, myProcesses]) + useEffect(() => { if (messageType === "new") { if (userId) { @@ -51,7 +85,6 @@ export default function Chat({ heightClass = "h-[calc(100vh-8rem)]" }: ChatProps setSelectedConversation(userId) setNewMessage(`${data.subject ? `[${data.subject}] ` : ""}${data.content}`) sessionStorage.removeItem("newMessage") - showNotification("info", `Conversation ouverte avec ${data.userName}`) } } else if (groupType === "group") { const groupData = sessionStorage.getItem("newGroupMessage") @@ -60,227 +93,44 @@ export default function Chat({ heightClass = "h-[calc(100vh-8rem)]" }: ChatProps setSelectedConversation("group-new") setNewMessage(`${data.subject ? `[${data.subject}] ` : ""}${data.content}`) sessionStorage.removeItem("newGroupMessage") - showNotification("info", `Conversation de groupe créée avec ${data.users.length} utilisateur(s)`) } } } }, [userId, messageType, groupType]) - const showNotification = (type: "success" | "error" | "info", message: string) => { - console.log(`${type.toUpperCase()}: ${message}`) - } - + // Create conversations array with pairing processes only const conversations = [ - { - id: "global", - name: "My Work", - type: "group", - avatar: "MW", - lastMessage: "Bienvenue sur le chat de l’espace My Work", - lastMessageTime: "Maintenant", - unreadCount: 0, - isOnline: false, - isTyping: false, - members: 0, - }, - { - id: "1", - name: "Marie Dubois", - type: "direct", - avatar: "MD", - lastMessage: "Parfait, merci pour la validation !", - lastMessageTime: "14:32", + ...pairingProcesses.map(process => { + // Generate avatar from memberPublicName or processId + let avatar = "P" // Default for Pairing + if (process.memberPublicName && typeof process.memberPublicName === 'string' && process.memberPublicName.trim().length > 0) { + // Use memberPublicName if not empty + avatar = process.memberPublicName.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2) + } else { + // Use first 2 chars of processId if memberPublicName is empty + avatar = process.id.slice(0, 2).toUpperCase() + } + + // Safe display name + const displayName = typeof process.memberPublicName === 'string' && process.memberPublicName.trim().length > 0 + ? process.memberPublicName + : `Membre ${process.id.slice(0, 8)}` + + return { + id: process.id, + name: displayName, + avatar: avatar, + lastMessage: "", + lastMessageTime: "", unreadCount: 0, isOnline: true, isTyping: false, - }, - { - id: "2", - name: "Équipe Juridique", - type: "group", - avatar: "EJ", - lastMessage: "IA DocV: Analyse terminée pour Contrat_Client_ABC.pdf", - lastMessageTime: "13:45", - unreadCount: 1, - isOnline: false, - isTyping: false, - members: 5, - }, - { - id: "3", - name: "Sophie Laurent", - type: "direct", - avatar: "SL", - lastMessage: "Pouvez-vous m'envoyer le rapport ?", - lastMessageTime: "12:20", - unreadCount: 1, - isOnline: false, - isTyping: false, - }, - { - id: "4", - name: "Direction", - type: "group", - avatar: "DIR", - lastMessage: "Réunion reportée à demain 10h", - lastMessageTime: "11:15", - unreadCount: 0, - isOnline: false, - isTyping: false, - members: 3, - }, - { - id: "5", - name: "Thomas Rousseau", - type: "direct", - avatar: "TR", - lastMessage: "Merci pour l'info !", - lastMessageTime: "Hier", - unreadCount: 0, - isOnline: true, - isTyping: true, - }, + pairingId: process.id + } + }) ] - const messages = [ - { - id: "1", - senderId: "marie", - senderName: "Marie Dubois", - content: "Bonjour ! J'ai besoin de votre avis sur le nouveau contrat client.", - timestamp: "14:20", - type: "text", - status: "read", - }, - { - id: "2", - senderId: "me", - senderName: "Moi", - content: "Bien sûr, pouvez-vous me l'envoyer ?", - timestamp: "14:22", - type: "text", - status: "read", - }, - { - id: "3", - senderId: "marie", - senderName: "Marie Dubois", - content: "", - timestamp: "14:25", - type: "file", - fileName: "Contrat_Client_ABC.pdf", - fileSize: "2.3 MB", - status: "read", - }, - { - id: "4", - senderId: "me", - senderName: "Moi", - content: - "J'ai relu le contrat, tout me semble correct. Les clauses de confidentialité sont bien définies.", - timestamp: "14:30", - type: "text", - status: "read", - }, - { - id: "5", - senderId: "marie", - senderName: "Marie Dubois", - content: "Parfait, merci pour la validation !", - timestamp: "14:32", - type: "text", - status: "delivered", - }, - { - id: "6", - senderId: "ai", - senderName: "IA DocV", - content: `📄 **Analyse IA du document "Contrat_Client_ABC.pdf"** - -**Type de document :** PDF (2.3 MB) -**Statut :** ✅ Validé -**Dernière modification :** Il y a 2 heures - -**📊 Analyse du contenu :** -• Document juridique détecté avec haute précision -• 3 tag(s) identifié(s) : contrat, client, juridique -• Résumé automatique disponible -• 47 pages analysées -• 12 clauses contractuelles détectées - -**🎯 Métriques de qualité :** -• Lisibilité : 92% -• Conformité juridique : 100% -• Sécurité documentaire : Maximale -• Complétude des informations : 95% - -**🔍 Points clés identifiés :** -• Durée du contrat : 12 mois -• Montant total : 150 000€ HT -• Clauses de confidentialité : ✅ Présentes et conformes -• Propriété intellectuelle : ✅ Bien définie -• Conditions de résiliation : ✅ Équilibrées - -**🛡️ Analyse de conformité RGPD :** -• Données personnelles : ⚠️ Détectées (coordonnées client) -• Durée de conservation : Conforme (7 ans) -• Droit à l'oubli : Applicable après expiration -• Consentement : ✅ Explicite - -**⚡ Recommandations :** -• ✅ Document prêt pour signature -• 📋 Archivage permanent recommandé -• 🔄 Révision suggérée dans 11 mois -• 📧 Notification client automatique activée - -**📈 Score global : 94/100** - -*Analyse générée automatiquement par l'IA DocV - Fiabilité : 98%*`, - timestamp: "14:35", - type: "ai_analysis", - status: "delivered", - analysisType: "document", - documentName: "Contrat_Client_ABC.pdf", - confidence: 98, - processingTime: "2.3s", - }, - { - id: "7", - senderId: "ai", - senderName: "IA DocV", - content: `🔍 **Analyse comparative - Dossier Contrats** - -**📊 Analyse de 8 documents similaires :** -• Contrats clients : 5 documents -• Avenants : 2 documents -• Conditions générales : 1 document - -**📈 Tendances identifiées :** -• Montant moyen des contrats : +15% vs trimestre précédent -• Durée moyenne : 14 mois (stable) -• Taux de renouvellement : 87% (↗️ +5%) - -**⚠️ Points d'attention :** -• 2 contrats expirent dans les 30 jours -• 1 clause de révision tarifaire à activer -• Mise à jour RGPD requise sur 3 documents - -**🎯 Actions recommandées :** -1. Planifier renouvellement contrats Q1 2024 -2. Standardiser les clauses de confidentialité -3. Créer un modèle basé sur ce contrat (performance optimale) - -*Analyse prédictive activée - Prochaine révision : 15 février 2024*`, - timestamp: "14:37", - type: "ai_analysis", - status: "delivered", - analysisType: "comparative", - confidence: 95, - processingTime: "4.1s", - }, - ] - - const filteredConversations = conversations + const messages: any[] = [] const currentConversation = conversations.find((conv) => conv.id === selectedConversation) @@ -389,11 +239,17 @@ export default function Chat({ heightClass = "h-[calc(100vh-8rem)]" }: ChatProps
- {filteredConversations.map((conversation) => ( + {isLoading ? ( +
+
Chargement des processus de pairing...
+
+ ) : filteredConversations.length > 0 ? ( + filteredConversations.map((conversation) => (
setSelectedConversation(conversation.id)} - className={`p-4 border-b cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 ${selectedConversation === conversation.id + className={`p-4 border-b cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700 ${ + selectedConversation === conversation.id ? "bg-blue-50 dark:bg-blue-900 border-r-2 border-blue-500 dark:border-blue-400" : "" }`} @@ -401,46 +257,20 @@ export default function Chat({ heightClass = "h-[calc(100vh-8rem)]" }: ChatProps
- {conversation.type === "group" ? ( - - ) : ( {conversation.avatar} - )}
- {conversation.isOnline && conversation.type === "direct" && ( + {conversation.isOnline && ( )}

{conversation.name}

- {conversation.lastMessageTime}
-
-

- {conversation.isTyping ? ( - En train d'écrire... - ) : ( - - {conversation.lastMessage} - - )} + {'pairingId' in conversation && ( +

+ ID: {conversation.pairingId.slice(0, 8)}...{conversation.pairingId.slice(-4)}

- {conversation.unreadCount > 0 && ( - - {conversation.unreadCount} - - )} -
- {conversation.type === "group" && ( -

{conversation.members} membres

)}
@@ -457,24 +287,16 @@ export default function Chat({ heightClass = "h-[calc(100vh-8rem)]" }: ChatProps
- {currentConversation.type === "group" ? ( - - ) : ( {currentConversation.avatar} - )}
- {currentConversation.isOnline && currentConversation.type === "direct" && ( + {currentConversation.isOnline && ( )}

{currentConversation.name}

- {currentConversation.type === "group" - ? `${currentConversation.members} membres` - : currentConversation.isOnline - ? "En ligne" - : "Hors ligne"} + {currentConversation.isOnline ? "En ligne" : "Hors ligne"}

@@ -487,44 +309,13 @@ export default function Chat({ heightClass = "h-[calc(100vh-8rem)]" }: ChatProps
- {messages.map((message) => ( -
- {message.type === "ai_analysis" ? ( - renderAIMessage(message) - ) : ( -
-
- {message.type === "text" ? ( -

{message.content}

- ) : message.type === "file" ? ( -
- -
-

{message.fileName}

-

{message.fileSize}

-
- -
- ) : null} -
- {message.timestamp} - {message.senderId === "me" &&
{getStatusIcon(message.status)}
} +
+
+ +

Aucun message

+

Commencez une conversation en envoyant un message

-
- )} -
- ))}