Now we can add a member to a folder with autocompletion

This commit is contained in:
Sadrinho27 2025-11-07 19:39:26 +01:00
parent a91ca01f14
commit c751809411
3 changed files with 110 additions and 24 deletions

View File

@ -96,7 +96,6 @@ export default function DashboardPage() {
const [folderType, setFolderType] = useState<FolderType | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [notification, setNotification] = useState<{ type: "success" | "error" | "info"; message: string } | null>(null)
const [selectedFolder, setSelectedFolder] = useState<EnrichedFolderData | null>(null);
const {
@ -104,6 +103,7 @@ export default function DashboardPage() {
userPairingId,
folders,
loadingFolders,
members,
setFolderProcesses,
setMyFolderProcesses,
setFolderPrivateData
@ -131,20 +131,32 @@ 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(() => {
// 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
]);
// 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 }));
@ -152,11 +164,12 @@ export default function DashboardPage() {
if (prevMyProcesses.includes(processId)) return prevMyProcesses;
return [...prevMyProcesses, processId];
});
setFolderPrivateData((prevData) => ({ ...prevData, [firstStateId]: folderData }));
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);
@ -319,6 +332,7 @@ export default function DashboardPage() {
onSave={handleSaveNewFolder}
onCancel={handleCloseModal}
folderType={folderType || "autre"}
members={members}
/>
)}
{notification && (

View File

@ -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<React.SetStateAction<FolderData>>
@ -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<FolderType, { bg: string; border: string; focus: string; button: string }> = {
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<FolderData>({ ...defaultFolder, ...folder });
const [currentNote, setCurrentNote] = useState('');
// --- NOUVEAU: État pour les membres sélectionnés ---
const [selectedMembers, setSelectedMembers] = useState<string[]>([]);
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({
</div>
</div>
{/* Membres */}
{!readOnly && (
<div className="space-y-4">
<h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100">Membres</h3>
<MemberAutocomplete
allMembers={members}
selectedMembers={selectedMembers}
onChange={setSelectedMembers} // On passe le setter de l'état
/>
</div>
)}
{/* Notes */}
<div className="space-y-4">
<h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100">Notes</h3>

View File

@ -32,10 +32,12 @@ type FourNKContextType = {
folderPrivateData: Record<string, Record<string, any>>;
folders: EnrichedFolderData[]; // <-- Utilise le type enrichi
loadingFolders: boolean;
members: string[];
setFolderProcesses: React.Dispatch<React.SetStateAction<any>>;
setMyFolderProcesses: React.Dispatch<React.SetStateAction<string[]>>;
setFolderPrivateData: React.Dispatch<React.SetStateAction<Record<string, Record<string, any>>>>;
setMembers: React.Dispatch<React.SetStateAction<string[]>>;
};
const FourNKContext = createContext<FourNKContextType | undefined>(undefined);
@ -44,6 +46,7 @@ export function FourNKProvider({ children }: { children: ReactNode }) {
const [isConnected, setIsConnected] = useState(false);
const [userPairingId, setUserPairingId] = useState<string | null>(null);
const [processes, setProcesses] = useState<any>(null);
const [members, setMembers] = useState<string[]>([]);;
const [myProcesses, setMyProcesses] = useState<string[]>([]);
const [folderProcesses, setFolderProcesses] = useState<any>(null);
const [myFolderProcesses, setMyFolderProcesses] = useState<string[]>([]);
@ -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 (