From c7518094115ecc8f68c3bd564f2345a2d3ee5103 Mon Sep 17 00:00:00 2001 From: Sadrinho27 Date: Fri, 7 Nov 2025 19:39:26 +0100 Subject: [PATCH] Now we can add a member to a folder with autocompletion --- app/dashboard/page.tsx | 52 +++++++++++++++++++++------------- components/4nk/FolderModal.tsx | 38 ++++++++++++++++++++++--- lib/contexts/FourNKContext.tsx | 44 +++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 24 deletions(-) diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index f5aff34..8d53ca3 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -96,7 +96,6 @@ export default function DashboardPage() { const [folderType, setFolderType] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [notification, setNotification] = useState<{ type: "success" | "error" | "info"; message: string } | null>(null) - const [selectedFolder, setSelectedFolder] = useState(null); const { @@ -104,6 +103,7 @@ export default function DashboardPage() { userPairingId, folders, loadingFolders, + members, setFolderProcesses, setMyFolderProcesses, setFolderPrivateData @@ -131,33 +131,46 @@ export default function DashboardPage() { }; const handleSaveNewFolder = useCallback( - (folderData: FolderData) => { + (folderData: FolderData, selectedMembers: string[]) => { if (!isConnected || !userPairingId) { showNotification("error", "Vous devez être connecté à 4NK pour créer un dossier"); return; } + + // Crée les rôles par défaut (probablement 'owner' = vous) const roles = setDefaultFolderRoles(userPairingId); const folderPrivateFields = FolderPrivateFields; - MessageBus.getInstance(iframeUrl) - .createFolder(folderData, folderPrivateFields, roles) - .then((_folderCreated: FolderCreated) => { - const firstStateId = _folderCreated.process.states[0].state_id; - MessageBus.getInstance(iframeUrl) - .notifyProcessUpdate(_folderCreated.processId, firstStateId) - .then(() => { - const { processId, process } = _folderCreated; - setFolderProcesses((prevProcesses: any) => ({ ...prevProcesses, [processId]: process })); - setMyFolderProcesses((prevMyProcesses: string[]) => { - if (prevMyProcesses.includes(processId)) return prevMyProcesses; - return [...prevMyProcesses, processId]; - }); - setFolderPrivateData((prevData) => ({ ...prevData, [firstStateId]: folderData })); + // Fusionne votre userPairingId avec les membres sélectionnés + // On utilise un Set pour éviter les doublons + const allOwnerMembers = new Set([ + ...roles.owner.members, // Membres par défaut (vous) + userPairingId, // S'assurer que vous y êtes + ...selectedMembers // Ajoute les nouveaux membres + ]); - showNotification("success", "Dossier créé avec succès !"); - handleCloseModal(); + // Met à jour la liste des membres pour le rôle 'owner' + // (Vous pouvez ajuster "owner" pour un autre rôle si nécessaire) + roles.owner.members = Array.from(allOwnerMembers); + console.log(roles); + + MessageBus.getInstance(iframeUrl).createFolder(folderData, folderPrivateFields, roles).then((_folderCreated: FolderCreated) => { + MessageBus.getInstance(iframeUrl).notifyProcessUpdate(_folderCreated.processId, _folderCreated.process.states[0].state_id).then(() => { + MessageBus.getInstance(iframeUrl).validateState(_folderCreated.processId, _folderCreated.process.states[0].state_id).then((_updatedProcess: any) => { + const { processId, process } = _folderCreated; + + setFolderProcesses((prevProcesses: any) => ({ ...prevProcesses, [processId]: process })); + setMyFolderProcesses((prevMyProcesses: string[]) => { + if (prevMyProcesses.includes(processId)) return prevMyProcesses; + return [...prevMyProcesses, processId]; }); - }) + setFolderPrivateData((prevData) => ({ ...prevData, [_folderCreated.process.states[0].state_id]: folderData })); + + showNotification("success", "Dossier créé avec succès !"); + handleCloseModal(); + }); + }); + }) .catch((error: any) => { console.error('Erreur lors de la création du dossier:', error); showNotification("error", "Erreur lors de la création du dossier"); @@ -319,6 +332,7 @@ export default function DashboardPage() { onSave={handleSaveNewFolder} onCancel={handleCloseModal} folderType={folderType || "autre"} + members={members} /> )} {notification && ( diff --git a/components/4nk/FolderModal.tsx b/components/4nk/FolderModal.tsx index 3ea1651..67f517f 100644 --- a/components/4nk/FolderModal.tsx +++ b/components/4nk/FolderModal.tsx @@ -1,17 +1,21 @@ import React, { useEffect, useState, memo } from 'react'; import Modal from './Modal'; import type { FolderData } from '@/lib/4nk/models/FolderData'; +import { MemberAutocomplete } from '../ui/member-autocomplete'; type FolderType = 'contrat' | 'projet' | 'rapport' | 'finance' | 'rh' | 'marketing' | 'autre'; interface FolderModalProps { folder?: FolderData; - onSave?: (folderData: FolderData) => void; + // --- MODIFIÉ --- + onSave?: (folderData: FolderData, selectedMembers: string[]) => void; onCancel?: () => void; readOnly?: boolean; isOpen: boolean; onClose: () => void; folderType?: FolderType; + // --- NOUVEAU --- + members?: string[]; // Liste des membres disponibles renderExtraFields?: ( folderData: FolderData, setFolderData: React.Dispatch> @@ -24,7 +28,9 @@ const defaultFolder: FolderData = { description: '', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), - notes: [] + notes: [], + messages: [], + messages_owner: [] }; function capitalize(s?: string) { @@ -32,7 +38,7 @@ function capitalize(s?: string) { return s.charAt(0).toUpperCase() + s.slice(1); } -// Mapping des couleurs par type de dossier +// Mapping des couleurs const folderColors: Record = { contrat: { bg: 'bg-blue-50 dark:bg-blue-900', border: 'border-blue-300 dark:border-blue-700', focus: 'focus:ring-blue-400 dark:focus:ring-blue-600', button: 'bg-blue-500 hover:bg-blue-600' }, projet: { bg: 'bg-green-50 dark:bg-green-900', border: 'border-green-300 dark:border-green-700', focus: 'focus:ring-green-400 dark:focus:ring-green-600', button: 'bg-green-500 hover:bg-green-600' }, @@ -51,15 +57,19 @@ function FolderModal({ isOpen, onClose, folderType = 'autre', + members = [], renderExtraFields }: FolderModalProps) { const [folderData, setFolderData] = useState({ ...defaultFolder, ...folder }); const [currentNote, setCurrentNote] = useState(''); + // --- NOUVEAU: État pour les membres sélectionnés --- + const [selectedMembers, setSelectedMembers] = useState([]); useEffect(() => { if (isOpen) { setFolderData({ ...defaultFolder, ...(folder || {}) }); setCurrentNote(''); + setSelectedMembers([]); // <-- MODIFIÉ: Réinitialise les membres } }, [isOpen, folder]); @@ -70,6 +80,14 @@ function FolderModal({ setFolderData(prev => ({ ...(prev as any), [name]: value } as FolderData)); }; + const handleMemberToggle = (memberId: string) => { + setSelectedMembers(prev => + prev.includes(memberId) + ? prev.filter(id => id !== memberId) + : [...prev, memberId] + ); + }; + const addNote = () => { const v = currentNote.trim(); if (!v) return; @@ -83,7 +101,7 @@ function FolderModal({ const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - onSave?.({ ...folderData, updated_at: new Date().toISOString() }); + onSave?.({ ...folderData, updated_at: new Date().toISOString() }, selectedMembers); onClose(); }; @@ -139,6 +157,18 @@ function FolderModal({ + {/* Membres */} + {!readOnly && ( +
+

Membres

+ +
+ )} + {/* Notes */}

Notes

diff --git a/lib/contexts/FourNKContext.tsx b/lib/contexts/FourNKContext.tsx index 561a460..f13042b 100644 --- a/lib/contexts/FourNKContext.tsx +++ b/lib/contexts/FourNKContext.tsx @@ -32,10 +32,12 @@ type FourNKContextType = { folderPrivateData: Record>; folders: EnrichedFolderData[]; // <-- Utilise le type enrichi loadingFolders: boolean; + members: string[]; setFolderProcesses: React.Dispatch>; setMyFolderProcesses: React.Dispatch>; setFolderPrivateData: React.Dispatch>>>; + setMembers: React.Dispatch>; }; const FourNKContext = createContext(undefined); @@ -44,6 +46,7 @@ export function FourNKProvider({ children }: { children: ReactNode }) { const [isConnected, setIsConnected] = useState(false); const [userPairingId, setUserPairingId] = useState(null); const [processes, setProcesses] = useState(null); + const [members, setMembers] = useState([]);; const [myProcesses, setMyProcesses] = useState([]); const [folderProcesses, setFolderProcesses] = useState(null); const [myFolderProcesses, setMyFolderProcesses] = useState([]); @@ -124,7 +127,39 @@ export function FourNKProvider({ children }: { children: ReactNode }) { setFolders(folderData); setLoadingFolders(false); } - }, [folderProcesses, myFolderProcesses, folderPrivateData, fetchFolderPrivateData]); + }, [folderProcesses, myFolderProcesses, folderPrivateData, fetchFolderPrivateData, setFolders, setLoadingFolders]); // J'ai ajouté setFolders et setLoadingFolders aux dépendances + + const loadMembersFrom4NK = useCallback(() => { + if (!processes || !userPairingId) return; + + const memberList: string[] = []; + const EXCLUDED_STATE_ID = "0000000000000000000000000000000000000000000000000000000000000000"; + + Object.entries(processes).forEach(([processId, process]: [string, any]) => { + const validStates = process.states.filter( + state => state && state.state_id !== EXCLUDED_STATE_ID + ); + if (validStates.length === 0) return; + + const referenceState = validStates.find( + state => state.pcd_commitment?.pairedAddresses + ); + if (!referenceState) return; + + const userAddress = referenceState.commited_in + + memberList.push(userAddress); + }); + + // Filtrer la liste pour enlever l'ID de l'utilisateur connecté + const filteredMemberList = memberList.filter( + member => member !== userPairingId + ); + + // Sauvegarder la liste filtrée dans l'état + setMembers(filteredMemberList); + + }, [processes, userPairingId]); // Chargement initial des données 4NK useEffect(() => { @@ -176,6 +211,11 @@ export function FourNKProvider({ children }: { children: ReactNode }) { loadFoldersFrom4NK(); }, [loadFoldersFrom4NK]); + // Re-calculer les membres lorsque les données changent + useEffect(() => { + loadMembersFrom4NK(); + }, [loadMembersFrom4NK]); + const value = { isConnected, @@ -187,9 +227,11 @@ export function FourNKProvider({ children }: { children: ReactNode }) { folderPrivateData, folders, loadingFolders, + members, setFolderProcesses, setMyFolderProcesses, setFolderPrivateData, + setMembers, }; return (