Compare commits

..

4 Commits

3 changed files with 117 additions and 74 deletions

View File

@ -1,6 +1,6 @@
"use client"
import { useState } from "react"
import { useState, useEffect } from "react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
@ -12,40 +12,36 @@ import {
Folder,
MessageSquare
} from "lucide-react"
import type { EnrichedFolderData } from "@/lib/contexts/FourNKContext";
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;
}
// Message fictif pour la maquette
interface MockMessage {
id: number;
sender: 'me' | 'other';
name: string;
avatar: string;
text: string;
time: string;
type: 'owner' | 'general'; // Pour filtrer
}
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[]>([]);
// Données fictives
const mockMessages: MockMessage[] = [
{ id: 1, sender: 'other', name: 'Membre A4B2 (Owner)', avatar: 'A4', text: "Validation Owner OK.", time: "14:30", type: 'owner' },
{ id: 2, sender: 'me', name: 'Vous', avatar: 'MO', text: "Parfait, merci.", time: "14:32", type: 'owner' },
{ id: 3, sender: 'other', name: 'Membre C8F1', avatar: 'C8', text: "Le client a une question sur ce dossier.", time: "14:33", type: 'general' },
{ id: 4, sender: 'me', name: 'Vous', avatar: 'MO', text: "Je regarde ça.", time: "14:34", type: 'general' },
];
const {
isConnected,
userPairingId,
folders,
members,
} = use4NK();
useEffect(() => {
setOwnerMessages(folder?.messages_owner || []);
setGeneralMessages(folder?.messages || []);
}, [folder]);
// Filtre les messages basé sur l'onglet actif
const filteredMessages = mockMessages.filter(msg => msg.type === activeTab);
const filteredMessages = activeTab === 'owner' ? ownerMessages : generalMessages;
const handleProcessUpdate = async (processId: string, key: string, value: any) => {
try {
@ -75,7 +71,7 @@ export default function FolderChat({ folder }: FolderChatProps) {
await messageBus.validateState(processId, newStateId);
// Refresh the processes data
// const updatedProcesses = await messageBus.getProcesses();
await messageBus.getProcesses();
console.log('Process updated successfully');
} catch (error) {
@ -85,12 +81,30 @@ export default function FolderChat({ folder }: FolderChatProps) {
};
const handleSendMessage = () => {
if (newMessage.trim()) {
console.log(`Envoi message [${activeTab}] à:`, folder?.folderNumber, "Msg:", newMessage)
// TODO: Implémenter la logique d'envoi de message
if(!folder) return;
if (newMessage.trim() && folder) {
console.log(`Envoi message [${activeTab}] dans le dossier:`, folder?.name, "Msg:", newMessage)
const key = activeTab === 'owner' ? 'messages_owner' : 'messages'
handleProcessUpdate(folder.processId, key, newMessage)
// Créez l'objet du nouveau message
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]);
}
// Envoi de la mise à jour au MessageBus (en arrière-plan)
handleProcessUpdate(folder.processId, key, newMessageData)
// Vider l'input
setNewMessage("")
}
}
@ -156,18 +170,16 @@ export default function FolderChat({ folder }: FolderChatProps) {
</Button>
</div>
{/* --- LISTE DES MEMBRES (SUPPRIMÉE) --- */}
{/* Zone des messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4 bg-gray-900">
{filteredMessages.length > 0 ? filteredMessages.map((msg) => (
{filteredMessages.length > 0 ? filteredMessages.map((msg, i) => (
<div
key={msg.id}
className={`flex items-start gap-3 ${msg.sender === 'me' ? 'justify-end' : ''}`}
key={i}
className={`flex items-start gap-3 ${msg.sender === userPairingId ? 'justify-end' : ''}`}
>
{msg.sender === 'other' && (
{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.avatar}</span>
<span className="text-xs text-blue-300 font-medium">{msg.sender.slice(0, 2)}</span>
</div>
)}
<div>
@ -177,13 +189,16 @@ export default function FolderChat({ folder }: FolderChatProps) {
: 'bg-gray-700 text-gray-100 rounded-bl-none'
}`}
>
{msg.sender === 'other' && (
<p className="text-xs font-medium text-blue-300 mb-1">{msg.name}</p>
{msg.sender != userPairingId && (
<p className="text-xs font-medium text-blue-300 mb-1">KAAK</p>
)}
<p>{msg.text}</p>
<p>{msg.message}</p>
</div>
<p className={`text-xs text-gray-500 mt-1 ${msg.sender === 'me' ? 'text-right' : ''}`}>
{msg.time}
<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>

View File

@ -1,5 +1,27 @@
import type { RoleDefinition } from "./Roles";
export interface FolderChatAttachment {
ext: string;
file_name: string;
title?: string;
type?: string;
category?: string;
base64: string;
note?: string;
}
export interface FolderChatData {
timestamp: number;
sender: string;
receiver: string;
fromRole: string
toRole: string
ia?: boolean;
title?: string;
message: string;
data?: FolderChatAttachment[];
}
export interface AttachedFile {
id: string;
name: string;
@ -16,8 +38,8 @@ export interface FolderData {
created_at: string;
updated_at: string;
notes: string[];
messages: string[];
messages_owner: string[];
messages: FolderChatData[];
messages_owner: FolderChatData[];
attachedFiles?: AttachedFile[];
}

View File

@ -4,7 +4,7 @@ import { createContext, useContext, useState, useEffect, useCallback, ReactNode
import MessageBus from "@/lib/4nk/MessageBus";
import { iframeUrl } from "@/app/page";
import UserStore from "@/lib/4nk/UserStore";
import { FolderData } from "@/lib/4nk/models/FolderData";
import { FolderChatData, FolderData } from "@/lib/4nk/models/FolderData";
// Interface enrichie qui inclut maintenant les membres ET les fichiers
export interface EnrichedFolderData extends FolderData {
@ -82,46 +82,52 @@ export function FourNKProvider({ children }: { children: ReactNode }) {
return;
}
// console.log(validStates);
let basePrivateData;
let mergedMessages: FolderChatData[] = [];
let mergedMessagesOwner: FolderChatData[] = [];
// validStates.forEach(state => {
hasFoldersToLoad = true;
const stateToProcess = referenceState;
validStates.forEach((state: any) => {
hasFoldersToLoad = true;
const stateToProcess = state;
const privateData = folderPrivateData[stateToProcess.state_id];
const privateData = folderPrivateData[stateToProcess.state_id];
// Si on n'a pas les données pour cet état de référence...
if (!privateData) {
hasAllPrivateData = false;
missingPrivateData.push({ processId, stateId: stateToProcess.state_id });
return; // On quitte ce process, on réessaiera au prochain rendu
}
// console.log("Données déchiffrées pour le state:", stateToProcess.state_id, privateData);
// Si on n'a pas les données pour cet état de référence...
if (!privateData) {
hasAllPrivateData = false;
missingPrivateData.push({ processId, stateId: stateToProcess.state_id });
return; // On quitte ce process, on réessaiera au prochain rendu
}
// console.log("Données déchiffrées pour le state:", stateToProcess.state_id, privateData);
/*
// 4. CONDITION B: On vérifie si cet état contient des messages.
const hasMessages = (privateData.messages && privateData.messages.length > 0);
const hasMessagesOwner = (privateData.messages_owner && privateData.messages_owner.length > 0);
if (privateData.messages_owner instanceof Map) {
const messageOwnerObj = Object.fromEntries(privateData.messages_owner);
mergedMessagesOwner.push(messageOwnerObj);
}
// Si cet état n'a pas de messages, on l'ignore (return)
if (!hasMessages && !hasMessagesOwner) {
return; // Passe à l'état suivant
}
*/
if (privateData.messages instanceof Map) {
const messageObj = Object.fromEntries(privateData.messages);
mergedMessages.push(messageObj);
}
if (privateData.folderNumber) {
basePrivateData = privateData;
}
});
if (!basePrivateData) return;
folderData.push({
processId: processId,
folderNumber: mainFolderNumber, // La clé unique
name: privateData.name || `Dossier ${mainFolderNumber}`,
description: privateData.description || '',
created_at: privateData.created_at || new Date().toISOString(),
updated_at: privateData.updated_at || new Date().toISOString(),
notes: privateData.notes || [],
messages: privateData.messages || [],
messages_owner: privateData.messages_owner || [],
attachedFiles: privateData.attachedFiles || [],
folderNumber: mainFolderNumber,
name: basePrivateData.name || `Dossier ${mainFolderNumber}`,
description: basePrivateData.description || '',
created_at: basePrivateData.created_at || new Date().toISOString(),
updated_at: basePrivateData.updated_at || new Date().toISOString(),
notes: basePrivateData.notes || [],
messages: mergedMessages || [],
messages_owner: mergedMessagesOwner || [],
attachedFiles: basePrivateData.attachedFiles || [],
});
// });
});
@ -137,7 +143,7 @@ export function FourNKProvider({ children }: { children: ReactNode }) {
setFolders(folderData);
setLoadingFolders(false);
}
}, [folderProcesses, myFolderProcesses, folderPrivateData, fetchFolderPrivateData, setFolders, setLoadingFolders]); // J'ai ajouté setFolders et setLoadingFolders aux dépendances
}, [folderProcesses, myFolderProcesses, folderPrivateData, fetchFolderPrivateData, setFolders, setLoadingFolders]);
const loadMembersFrom4NK = useCallback(() => {
if (!processes || !userPairingId) return;