Now we can add a member to a folder with autocompletion
This commit is contained in:
parent
a91ca01f14
commit
c751809411
@ -96,7 +96,6 @@ export default function DashboardPage() {
|
|||||||
const [folderType, setFolderType] = useState<FolderType | null>(null);
|
const [folderType, setFolderType] = useState<FolderType | null>(null);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [notification, setNotification] = useState<{ type: "success" | "error" | "info"; message: string } | null>(null)
|
const [notification, setNotification] = useState<{ type: "success" | "error" | "info"; message: string } | null>(null)
|
||||||
|
|
||||||
const [selectedFolder, setSelectedFolder] = useState<EnrichedFolderData | null>(null);
|
const [selectedFolder, setSelectedFolder] = useState<EnrichedFolderData | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -104,6 +103,7 @@ export default function DashboardPage() {
|
|||||||
userPairingId,
|
userPairingId,
|
||||||
folders,
|
folders,
|
||||||
loadingFolders,
|
loadingFolders,
|
||||||
|
members,
|
||||||
setFolderProcesses,
|
setFolderProcesses,
|
||||||
setMyFolderProcesses,
|
setMyFolderProcesses,
|
||||||
setFolderPrivateData
|
setFolderPrivateData
|
||||||
@ -131,33 +131,46 @@ export default function DashboardPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveNewFolder = useCallback(
|
const handleSaveNewFolder = useCallback(
|
||||||
(folderData: FolderData) => {
|
(folderData: FolderData, selectedMembers: string[]) => {
|
||||||
if (!isConnected || !userPairingId) {
|
if (!isConnected || !userPairingId) {
|
||||||
showNotification("error", "Vous devez être connecté à 4NK pour créer un dossier");
|
showNotification("error", "Vous devez être connecté à 4NK pour créer un dossier");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crée les rôles par défaut (probablement 'owner' = vous)
|
||||||
const roles = setDefaultFolderRoles(userPairingId);
|
const roles = setDefaultFolderRoles(userPairingId);
|
||||||
const folderPrivateFields = FolderPrivateFields;
|
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 }));
|
// Fusionne votre userPairingId avec les membres sélectionnés
|
||||||
setMyFolderProcesses((prevMyProcesses: string[]) => {
|
// On utilise un Set pour éviter les doublons
|
||||||
if (prevMyProcesses.includes(processId)) return prevMyProcesses;
|
const allOwnerMembers = new Set([
|
||||||
return [...prevMyProcesses, processId];
|
...roles.owner.members, // Membres par défaut (vous)
|
||||||
});
|
userPairingId, // S'assurer que vous y êtes
|
||||||
setFolderPrivateData((prevData) => ({ ...prevData, [firstStateId]: folderData }));
|
...selectedMembers // Ajoute les nouveaux membres
|
||||||
|
]);
|
||||||
|
|
||||||
showNotification("success", "Dossier créé avec succès !");
|
// Met à jour la liste des membres pour le rôle 'owner'
|
||||||
handleCloseModal();
|
// (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) => {
|
.catch((error: any) => {
|
||||||
console.error('Erreur lors de la création du dossier:', error);
|
console.error('Erreur lors de la création du dossier:', error);
|
||||||
showNotification("error", "Erreur lors de la création du dossier");
|
showNotification("error", "Erreur lors de la création du dossier");
|
||||||
@ -319,6 +332,7 @@ export default function DashboardPage() {
|
|||||||
onSave={handleSaveNewFolder}
|
onSave={handleSaveNewFolder}
|
||||||
onCancel={handleCloseModal}
|
onCancel={handleCloseModal}
|
||||||
folderType={folderType || "autre"}
|
folderType={folderType || "autre"}
|
||||||
|
members={members}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{notification && (
|
{notification && (
|
||||||
|
|||||||
@ -1,17 +1,21 @@
|
|||||||
import React, { useEffect, useState, memo } from 'react';
|
import React, { useEffect, useState, memo } from 'react';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import type { FolderData } from '@/lib/4nk/models/FolderData';
|
import type { FolderData } from '@/lib/4nk/models/FolderData';
|
||||||
|
import { MemberAutocomplete } from '../ui/member-autocomplete';
|
||||||
|
|
||||||
type FolderType = 'contrat' | 'projet' | 'rapport' | 'finance' | 'rh' | 'marketing' | 'autre';
|
type FolderType = 'contrat' | 'projet' | 'rapport' | 'finance' | 'rh' | 'marketing' | 'autre';
|
||||||
|
|
||||||
interface FolderModalProps {
|
interface FolderModalProps {
|
||||||
folder?: FolderData;
|
folder?: FolderData;
|
||||||
onSave?: (folderData: FolderData) => void;
|
// --- MODIFIÉ ---
|
||||||
|
onSave?: (folderData: FolderData, selectedMembers: string[]) => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
folderType?: FolderType;
|
folderType?: FolderType;
|
||||||
|
// --- NOUVEAU ---
|
||||||
|
members?: string[]; // Liste des membres disponibles
|
||||||
renderExtraFields?: (
|
renderExtraFields?: (
|
||||||
folderData: FolderData,
|
folderData: FolderData,
|
||||||
setFolderData: React.Dispatch<React.SetStateAction<FolderData>>
|
setFolderData: React.Dispatch<React.SetStateAction<FolderData>>
|
||||||
@ -24,7 +28,9 @@ const defaultFolder: FolderData = {
|
|||||||
description: '',
|
description: '',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
notes: []
|
notes: [],
|
||||||
|
messages: [],
|
||||||
|
messages_owner: []
|
||||||
};
|
};
|
||||||
|
|
||||||
function capitalize(s?: string) {
|
function capitalize(s?: string) {
|
||||||
@ -32,7 +38,7 @@ function capitalize(s?: string) {
|
|||||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
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 }> = {
|
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' },
|
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' },
|
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,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
folderType = 'autre',
|
folderType = 'autre',
|
||||||
|
members = [],
|
||||||
renderExtraFields
|
renderExtraFields
|
||||||
}: FolderModalProps) {
|
}: FolderModalProps) {
|
||||||
const [folderData, setFolderData] = useState<FolderData>({ ...defaultFolder, ...folder });
|
const [folderData, setFolderData] = useState<FolderData>({ ...defaultFolder, ...folder });
|
||||||
const [currentNote, setCurrentNote] = useState('');
|
const [currentNote, setCurrentNote] = useState('');
|
||||||
|
// --- NOUVEAU: État pour les membres sélectionnés ---
|
||||||
|
const [selectedMembers, setSelectedMembers] = useState<string[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setFolderData({ ...defaultFolder, ...(folder || {}) });
|
setFolderData({ ...defaultFolder, ...(folder || {}) });
|
||||||
setCurrentNote('');
|
setCurrentNote('');
|
||||||
|
setSelectedMembers([]); // <-- MODIFIÉ: Réinitialise les membres
|
||||||
}
|
}
|
||||||
}, [isOpen, folder]);
|
}, [isOpen, folder]);
|
||||||
|
|
||||||
@ -70,6 +80,14 @@ function FolderModal({
|
|||||||
setFolderData(prev => ({ ...(prev as any), [name]: value } as FolderData));
|
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 addNote = () => {
|
||||||
const v = currentNote.trim();
|
const v = currentNote.trim();
|
||||||
if (!v) return;
|
if (!v) return;
|
||||||
@ -83,7 +101,7 @@ function FolderModal({
|
|||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onSave?.({ ...folderData, updated_at: new Date().toISOString() });
|
onSave?.({ ...folderData, updated_at: new Date().toISOString() }, selectedMembers);
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -139,6 +157,18 @@ function FolderModal({
|
|||||||
</div>
|
</div>
|
||||||
</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 */}
|
{/* Notes */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100">Notes</h3>
|
<h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100">Notes</h3>
|
||||||
|
|||||||
@ -32,10 +32,12 @@ type FourNKContextType = {
|
|||||||
folderPrivateData: Record<string, Record<string, any>>;
|
folderPrivateData: Record<string, Record<string, any>>;
|
||||||
folders: EnrichedFolderData[]; // <-- Utilise le type enrichi
|
folders: EnrichedFolderData[]; // <-- Utilise le type enrichi
|
||||||
loadingFolders: boolean;
|
loadingFolders: boolean;
|
||||||
|
members: string[];
|
||||||
|
|
||||||
setFolderProcesses: React.Dispatch<React.SetStateAction<any>>;
|
setFolderProcesses: React.Dispatch<React.SetStateAction<any>>;
|
||||||
setMyFolderProcesses: React.Dispatch<React.SetStateAction<string[]>>;
|
setMyFolderProcesses: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
setFolderPrivateData: React.Dispatch<React.SetStateAction<Record<string, Record<string, any>>>>;
|
setFolderPrivateData: React.Dispatch<React.SetStateAction<Record<string, Record<string, any>>>>;
|
||||||
|
setMembers: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FourNKContext = createContext<FourNKContextType | undefined>(undefined);
|
const FourNKContext = createContext<FourNKContextType | undefined>(undefined);
|
||||||
@ -44,6 +46,7 @@ export function FourNKProvider({ children }: { children: ReactNode }) {
|
|||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
const [userPairingId, setUserPairingId] = useState<string | null>(null);
|
const [userPairingId, setUserPairingId] = useState<string | null>(null);
|
||||||
const [processes, setProcesses] = useState<any>(null);
|
const [processes, setProcesses] = useState<any>(null);
|
||||||
|
const [members, setMembers] = useState<string[]>([]);;
|
||||||
const [myProcesses, setMyProcesses] = useState<string[]>([]);
|
const [myProcesses, setMyProcesses] = useState<string[]>([]);
|
||||||
const [folderProcesses, setFolderProcesses] = useState<any>(null);
|
const [folderProcesses, setFolderProcesses] = useState<any>(null);
|
||||||
const [myFolderProcesses, setMyFolderProcesses] = useState<string[]>([]);
|
const [myFolderProcesses, setMyFolderProcesses] = useState<string[]>([]);
|
||||||
@ -124,7 +127,39 @@ export function FourNKProvider({ children }: { children: ReactNode }) {
|
|||||||
setFolders(folderData);
|
setFolders(folderData);
|
||||||
setLoadingFolders(false);
|
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
|
// Chargement initial des données 4NK
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -176,6 +211,11 @@ export function FourNKProvider({ children }: { children: ReactNode }) {
|
|||||||
loadFoldersFrom4NK();
|
loadFoldersFrom4NK();
|
||||||
}, [loadFoldersFrom4NK]);
|
}, [loadFoldersFrom4NK]);
|
||||||
|
|
||||||
|
// Re-calculer les membres lorsque les données changent
|
||||||
|
useEffect(() => {
|
||||||
|
loadMembersFrom4NK();
|
||||||
|
}, [loadMembersFrom4NK]);
|
||||||
|
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
isConnected,
|
isConnected,
|
||||||
@ -187,9 +227,11 @@ export function FourNKProvider({ children }: { children: ReactNode }) {
|
|||||||
folderPrivateData,
|
folderPrivateData,
|
||||||
folders,
|
folders,
|
||||||
loadingFolders,
|
loadingFolders,
|
||||||
|
members,
|
||||||
setFolderProcesses,
|
setFolderProcesses,
|
||||||
setMyFolderProcesses,
|
setMyFolderProcesses,
|
||||||
setFolderPrivateData,
|
setFolderPrivateData,
|
||||||
|
setMembers,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user