Create FolderChat component

This commit is contained in:
Omar Oughriss 2025-10-24 17:07:10 +02:00
parent 22a1052080
commit 7982f7b300

View File

@ -0,0 +1,338 @@
"use client"
import { useState, useEffect } from "react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import {
MessageSquare,
Search,
Send,
Paperclip,
Smile,
MoreHorizontal,
Users,
Circle,
Folder,
Clock,
} from "lucide-react"
interface FolderMember {
id: string
name: string
avatar: string
isOnline: boolean
}
interface FolderConversation {
id: string
folderNumber: string
folderName: string
members: FolderMember[]
lastMessage: string
lastMessageTime: string
unreadCount: number
}
interface FolderChatProps {
heightClass?: string
folderProcesses?: any
myFolderProcesses?: string[]
folderPrivateData?: Record<string, Record<string, any>>
}
export default function FolderChat({
heightClass = "h-[calc(100vh-8rem)]",
folderProcesses,
myFolderProcesses = [],
folderPrivateData = {}
}: FolderChatProps) {
const [selectedConversation, setSelectedConversation] = useState("")
const [newMessage, setNewMessage] = useState("")
const [folderConversations, setFolderConversations] = useState<FolderConversation[]>([])
const [isLoading, setIsLoading] = useState(true)
const [searchQuery, setSearchQuery] = useState("")
// Extract folder conversations from processes
useEffect(() => {
if (folderProcesses && Object.keys(folderProcesses).length > 0) {
setIsLoading(true)
const conversations: FolderConversation[] = []
Object.entries(folderProcesses).forEach(([processId, process]: [string, any]) => {
// Only include processes that belong to the user
if (!myFolderProcesses.includes(processId)) return
const latestState = process.states?.[0]
if (!latestState) return
// Check if this process has a folderNumber (indicates it's a folder process)
const folderNumber = latestState.pcd_commitment?.folderNumber
if (!folderNumber) return
// Get private data for folder name
const privateData = folderPrivateData[latestState.state_id]
const folderName = privateData?.name || `Dossier ${folderNumber}`
// Extract members from roles.owner.members
const ownerMembers = latestState.roles?.owner?.members || []
const members: FolderMember[] = ownerMembers.map((memberId: string, index: number) => {
// Generate avatar from member ID
const avatar = memberId.slice(0, 2).toUpperCase()
return {
id: memberId,
name: `Membre ${memberId.slice(0, 8)}`, // Could be enhanced with real names
avatar: avatar,
isOnline: Math.random() > 0.5 // Random online status for demo
}
})
conversations.push({
id: processId,
folderNumber: folderNumber,
folderName: folderName,
members: members,
lastMessage: "",
lastMessageTime: "",
unreadCount: 0
})
})
setFolderConversations(conversations)
setIsLoading(false)
} else {
setIsLoading(true)
setFolderConversations([])
}
}, [folderProcesses, myFolderProcesses, folderPrivateData])
// Filter conversations based on search query
const filteredConversations = folderConversations.filter(conversation => {
if (!searchQuery.trim()) return true
const matchesNumber = conversation.folderNumber.toLowerCase().includes(searchQuery.toLowerCase())
const matchesName = conversation.folderName.toLowerCase().includes(searchQuery.toLowerCase())
const matchesId = conversation.id.toLowerCase().includes(searchQuery.toLowerCase())
return matchesNumber || matchesName || matchesId
})
const currentConversation = folderConversations.find((conv) => conv.id === selectedConversation)
const handleSendMessage = () => {
if (newMessage.trim()) {
console.log("Sending message to folder:", selectedConversation, "Message:", newMessage)
// Here implement the actual message sending logic
setNewMessage("")
}
}
return (
<div className={`${heightClass} flex`}>
{/* Sidebar with folder conversations */}
<div className="w-80 border-r bg-white dark:bg-gray-800 flex flex-col">
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
<div className="mb-4">
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">
Chat Dossiers
</h2>
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
<Input
placeholder="Rechercher un dossier..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 bg-gray-50 dark:bg-gray-700 border-gray-200 dark:border-gray-600"
/>
</div>
</div>
</div>
<div className="flex-1 overflow-y-auto">
{isLoading ? (
<div className="flex items-center justify-center p-8">
<div className="text-gray-500 dark:text-gray-400">Chargement des dossiers...</div>
</div>
) : filteredConversations.length > 0 ? (
filteredConversations.map((conversation) => (
<div
key={conversation.id}
onClick={() => setSelectedConversation(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"
: ""
}`}
>
<div className="flex items-start space-x-3">
<div className="relative">
<div className="w-12 h-12 bg-green-100 dark:bg-green-800 rounded-full flex items-center justify-center">
<Folder className="h-6 w-6 text-green-600 dark:text-green-400" />
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between">
<h3 className="font-medium text-gray-900 dark:text-gray-100 truncate">
{conversation.folderName}
</h3>
{conversation.members.length > 0 && (
<Badge variant="secondary" className="ml-2">
<Users className="h-3 w-3 mr-1" />
{conversation.members.length}
</Badge>
)}
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
#{conversation.folderNumber}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
{conversation.members.length} membre{conversation.members.length > 1 ? 's' : ''}
</p>
</div>
</div>
</div>
))
) : (
<div className="flex items-center justify-center p-8">
<div className="text-center">
<Folder className="h-8 w-8 mx-auto text-gray-400 dark:text-gray-500 mb-2" />
<p className="text-gray-500 dark:text-gray-400">Aucun dossier trouvé</p>
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">
Essayez de rechercher par nom ou numéro
</p>
</div>
</div>
)}
</div>
</div>
{/* Main chat area */}
<div className="flex-1 flex flex-col">
{currentConversation ? (
<>
{/* Chat header */}
<div className="p-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-green-100 dark:bg-green-800 rounded-full flex items-center justify-center">
<Folder className="h-5 w-5 text-green-600 dark:text-green-400" />
</div>
<div>
<h3 className="font-medium text-gray-900 dark:text-gray-100">
{currentConversation.folderName}
</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">
Dossier #{currentConversation.folderNumber} {currentConversation.members.length} membre{currentConversation.members.length > 1 ? 's' : ''}
</p>
</div>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline" size="sm">
<Users className="h-4 w-4" />
</Button>
<Button variant="outline" size="sm">
<MoreHorizontal className="h-4 w-4" />
</Button>
</div>
</div>
{/* Members list */}
{currentConversation.members.length > 0 && (
<div className="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
<div className="flex items-center space-x-2 mb-2">
<Users className="h-4 w-4 text-gray-500" />
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
Membres du dossier
</span>
</div>
<div className="flex flex-wrap gap-2">
{currentConversation.members.map((member) => (
<div
key={member.id}
className="flex items-center space-x-2 bg-gray-100 dark:bg-gray-700 rounded-full px-3 py-1"
>
<div className="relative">
<div className="w-6 h-6 bg-blue-100 dark:bg-blue-800 rounded-full flex items-center justify-center">
<span className="text-xs text-blue-600 dark:text-blue-400 font-medium">
{member.avatar}
</span>
</div>
{member.isOnline && (
<Circle className="absolute -bottom-1 -right-1 h-3 w-3 text-green-500 fill-current" />
)}
</div>
<span className="text-xs text-gray-700 dark:text-gray-300">
{member.name}
</span>
</div>
))}
</div>
</div>
)}
</div>
{/* Messages area */}
<div className="flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50 dark:bg-gray-900">
<div className="flex items-center justify-center h-full">
<div className="text-center">
<MessageSquare className="h-12 w-12 mx-auto text-gray-400 dark:text-gray-500 mb-4" />
<h3 className="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">
Aucun message
</h3>
<p className="text-gray-600 dark:text-gray-400">
Commencez une conversation avec les membres de ce dossier
</p>
</div>
</div>
</div>
{/* Message input */}
<div className="p-4 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
<div className="flex items-end space-x-2">
<Button variant="outline" size="sm">
<Paperclip className="h-4 w-4" />
</Button>
<div className="flex-1">
<Textarea
placeholder="Tapez votre message..."
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
}}
rows={1}
className="resize-none bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-700"
/>
</div>
<Button variant="outline" size="sm">
<Smile className="h-4 w-4" />
</Button>
<Button onClick={handleSendMessage} disabled={!newMessage.trim()}>
<Send className="h-4 w-4" />
</Button>
</div>
</div>
</>
) : (
<div className="flex-1 flex items-center justify-center bg-gray-50 dark:bg-gray-900">
<div className="text-center">
<Folder className="h-12 w-12 mx-auto text-gray-400 dark:text-gray-500 mb-4" />
<h3 className="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">
Sélectionnez un dossier
</h3>
<p className="text-gray-600 dark:text-gray-400">
Choisissez un dossier pour commencer à discuter avec ses membres
</p>
</div>
</div>
)}
</div>
</div>
)
}