Restore complete 4NK integration workflow
This commit is contained in:
parent
6d65014a45
commit
8b6b62e643
@ -131,61 +131,166 @@ export default function FoldersPage() {
|
||||
|
||||
// 4NK Integration useEffects
|
||||
useEffect(() => {
|
||||
// Load folders from 4NK when folder processes are available
|
||||
const userStore = UserStore.getInstance();
|
||||
const connected = userStore.isConnected();
|
||||
const pairingId = userStore.getUserPairingId();
|
||||
|
||||
console.log('Initial 4NK state:', { connected, pairingId });
|
||||
setIsConnected(connected);
|
||||
setUserPairingId(pairingId);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleConnectionFlow = async () => {
|
||||
if (!isConnected) return;
|
||||
|
||||
try {
|
||||
const messageBus = MessageBus.getInstance(iframeUrl);
|
||||
await messageBus.isReady();
|
||||
|
||||
const userStore = UserStore.getInstance();
|
||||
let pairingId = userStore.getUserPairingId();
|
||||
|
||||
// 1️⃣ Créer ou récupérer le pairing
|
||||
if (!pairingId) {
|
||||
pairingId = await messageBus.createUserPairing();
|
||||
console.log("✅ Pairing created:", pairingId);
|
||||
|
||||
if (pairingId) {
|
||||
userStore.pair(pairingId);
|
||||
setUserPairingId(pairingId);
|
||||
}
|
||||
} else {
|
||||
console.log("🔗 Already paired with ID:", pairingId);
|
||||
}
|
||||
|
||||
// 2️⃣ Charger les processes
|
||||
const processes = await messageBus.getProcesses();
|
||||
setProcesses(processes);
|
||||
setFolderProcesses(processes);
|
||||
|
||||
// 3️⃣ Charger les myProcesses
|
||||
const myProcesses = await messageBus.getMyProcesses();
|
||||
setMyProcesses(myProcesses);
|
||||
setMyFolderProcesses(myProcesses);
|
||||
|
||||
} catch (err) {
|
||||
console.error("❌ Error during pairing or process loading:", err);
|
||||
}
|
||||
};
|
||||
|
||||
handleConnectionFlow();
|
||||
}, [isConnected, iframeUrl]);
|
||||
|
||||
// Load folders from 4NK when folder processes are available
|
||||
useEffect(() => {
|
||||
if (folderProcesses && myFolderProcesses.length >= 0) {
|
||||
loadFoldersFrom4NK();
|
||||
} else {
|
||||
// Fallback: load empty folders if not connected to 4NK
|
||||
const mockFolders: FolderData[] = []
|
||||
setFolders(mockFolders)
|
||||
setStats({
|
||||
total: mockFolders.length,
|
||||
shared: mockFolders.filter((folder) => folder.access === "shared").length,
|
||||
private: mockFolders.filter((folder) => folder.access === "private").length,
|
||||
thisWeek: mockFolders.filter((folder) => {
|
||||
const weekAgo = new Date()
|
||||
weekAgo.setDate(weekAgo.getDate() - 7)
|
||||
return folder.modified > weekAgo
|
||||
}).length,
|
||||
permanent: mockFolders.filter((folder) => folder.storageType === "permanent").length,
|
||||
temporary: mockFolders.filter((folder) => folder.storageType === "temporary").length,
|
||||
})
|
||||
}
|
||||
}, [folderProcesses, myFolderProcesses, loadFoldersFrom4NK])
|
||||
}, [folderProcesses, myFolderProcesses, loadFoldersFrom4NK]);
|
||||
|
||||
// Update folders when private data changes
|
||||
useEffect(() => {
|
||||
if (folderProcesses && Object.keys(folderPrivateData).length > 0) {
|
||||
loadFoldersFrom4NK();
|
||||
}
|
||||
}, [folderPrivateData, folderProcesses, loadFoldersFrom4NK])
|
||||
}, [folderPrivateData, loadFoldersFrom4NK, folderProcesses]);
|
||||
|
||||
// 4NK Authentication handlers
|
||||
const handleLogin = useCallback(() => {
|
||||
setShowAuthModal(true);
|
||||
|
||||
}, []);
|
||||
|
||||
const handleLogout = useCallback(() => {
|
||||
UserStore.getInstance().disconnect();
|
||||
setIsConnected(false);
|
||||
setProcesses(null);
|
||||
setMyProcesses([]);
|
||||
setUserPairingId(null);
|
||||
// Filter and sort folders
|
||||
const filteredFolders = folders.filter(folder => {
|
||||
const matchesSearch = folder.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
folder.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
folder.folderNumber.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
|
||||
// Clear folder-related states
|
||||
setFolderProcesses(null);
|
||||
setMyFolderProcesses([]);
|
||||
setFolderPrivateData({});
|
||||
setFolders([]);
|
||||
setLoadingFolders(false);
|
||||
return matchesSearch
|
||||
})
|
||||
|
||||
// Émettre un événement pour vider les messages locaux
|
||||
EventBus.getInstance().emit('CLEAR_CONSOLE');
|
||||
const sortedFolders = [...filteredFolders].sort((a, b) => {
|
||||
let aValue: any, bValue: any
|
||||
|
||||
switch (sortBy) {
|
||||
case "name":
|
||||
aValue = a.name.toLowerCase()
|
||||
bValue = b.name.toLowerCase()
|
||||
break
|
||||
case "created_at":
|
||||
aValue = new Date(a.created_at)
|
||||
bValue = new Date(b.created_at)
|
||||
break
|
||||
case "updated_at":
|
||||
aValue = new Date(a.updated_at)
|
||||
bValue = new Date(b.updated_at)
|
||||
break
|
||||
default:
|
||||
aValue = a.name.toLowerCase()
|
||||
bValue = b.name.toLowerCase()
|
||||
}
|
||||
|
||||
if (sortOrder === "asc") {
|
||||
return aValue < bValue ? -1 : aValue > bValue ? 1 : 0
|
||||
} else {
|
||||
return aValue > bValue ? -1 : aValue < bValue ? 1 : 0
|
||||
}
|
||||
})
|
||||
|
||||
showNotification("info", "Déconnexion réussie");
|
||||
}, []);
|
||||
|
||||
// Modal handlers
|
||||
const handleOpenModal = (type: FolderType) => {
|
||||
setFolderType(type);
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsModalOpen(false);
|
||||
setFolderType(null);
|
||||
};
|
||||
|
||||
const handleSaveNewFolder = useCallback(
|
||||
(folderData: FolderData) => {
|
||||
if (!isConnected || !userPairingId) {
|
||||
console.error('Conditions non remplies:', { isConnected, userPairingId });
|
||||
showNotification(
|
||||
"error",
|
||||
`Vous devez être connecté à 4NK pour créer un dossier (Connected: ${isConnected}, PairingId: ${userPairingId ? 'OK' : 'NULL'})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
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(async () => {
|
||||
// Recharger les processes et myProcesses
|
||||
const messageBus = MessageBus.getInstance(iframeUrl);
|
||||
const [processes, myProcesses] = await Promise.all([
|
||||
messageBus.getProcesses(),
|
||||
messageBus.getMyProcesses()
|
||||
]);
|
||||
|
||||
setProcesses(processes);
|
||||
setFolderProcesses(processes);
|
||||
setMyProcesses(myProcesses);
|
||||
setMyFolderProcesses(myProcesses);
|
||||
|
||||
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");
|
||||
});
|
||||
},
|
||||
[isConnected, userPairingId, loadFoldersFrom4NK]
|
||||
);
|
||||
|
||||
// Auth connection handler
|
||||
const handleAuthConnect = useCallback(() => {
|
||||
setIsConnected(true);
|
||||
setShowAuthModal(false);
|
||||
@ -198,694 +303,6 @@ export default function FoldersPage() {
|
||||
}, []);
|
||||
|
||||
|
||||
// Fonction pour envoyer une notification dans le chat du dossier
|
||||
const sendFolderChatNotification = (folderId: string, message: string, actionType: string) => {
|
||||
const folderUsers = users.filter((user) => user.folderRoles[folderId])
|
||||
|
||||
console.log("Notification envoyée dans le chat du dossier:", {
|
||||
folderId,
|
||||
recipients: folderUsers.map((u) => ({ name: u.name, role: u.folderRoles[folderId]?.role })),
|
||||
message,
|
||||
actionType,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
|
||||
folderUsers.forEach((user) => {
|
||||
console.log(`📱 Notification push envoyée à ${user.name} (${user.email})`)
|
||||
})
|
||||
}
|
||||
|
||||
// Fonction pour organiser les utilisateurs par rôles
|
||||
const organizeUsersForInvitation = (currentFolderId: string) => {
|
||||
const organized = {
|
||||
folderRoles: {} as { [role: string]: UserWithRoles[] },
|
||||
spaceRoles: {} as { [role: string]: UserWithRoles[] },
|
||||
otherSpaces: {} as { [spaceName: string]: UserWithRoles[] },
|
||||
}
|
||||
|
||||
users.forEach((user) => {
|
||||
if (user.folderRoles[currentFolderId]) {
|
||||
const role = user.folderRoles[currentFolderId].role
|
||||
if (!organized.folderRoles[role]) organized.folderRoles[role] = []
|
||||
organized.folderRoles[role].push(user)
|
||||
}
|
||||
|
||||
const spaceRole = user.spaceRole
|
||||
if (!organized.spaceRoles[spaceRole]) organized.spaceRoles[spaceRole] = []
|
||||
organized.spaceRoles[spaceRole].push(user)
|
||||
|
||||
Object.values(user.spaceRoles).forEach((spaceInfo) => {
|
||||
if (spaceInfo.spaceName !== "Espace Principal") {
|
||||
if (!organized.otherSpaces[spaceInfo.spaceName]) organized.otherSpaces[spaceInfo.spaceName] = []
|
||||
organized.otherSpaces[spaceInfo.spaceName].push(user)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return organized
|
||||
}
|
||||
|
||||
// Folder actions
|
||||
const handleOpenFolder = (folder: FolderData) => {
|
||||
// Rediriger vers la page documents avec le filtre du dossier
|
||||
router.push(`/dashboard/documents?folder=${encodeURIComponent(folder.name)}`)
|
||||
}
|
||||
|
||||
const handleInviteFolder = (folder: FolderData) => {
|
||||
setInviteMessage("")
|
||||
setSelectedUser("")
|
||||
setSelectedRole("")
|
||||
setInviteScope("user")
|
||||
setActionModal({ type: "invite", folder, folders: [] })
|
||||
}
|
||||
|
||||
const handleArchiveFolder = (folder: FolderData) => {
|
||||
setArchiveReason("")
|
||||
setRetentionPeriod("5")
|
||||
setActionModal({ type: "archive", folder, folders: [] })
|
||||
}
|
||||
|
||||
const handleStorageConfig = (folder: FolderData) => {
|
||||
setStorageDuration(folder.temporaryStorageConfig?.duration.toString() || "30")
|
||||
setDataUsage(folder.temporaryStorageConfig?.dataUsage || "")
|
||||
setThirdPartyAccess(folder.temporaryStorageConfig?.thirdPartyAccess || "")
|
||||
setActionModal({ type: "storage_config", folder, folders: [] })
|
||||
}
|
||||
|
||||
const handleAIAnalysis = (folder: FolderData) => {
|
||||
showNotification("info", `Analyse IA en cours pour ${folder.name}...`)
|
||||
|
||||
// Simuler une analyse IA
|
||||
setTimeout(
|
||||
() => {
|
||||
const analysisResults = [
|
||||
`📊 **Analyse du dossier "${folder.name}"**\n\n` +
|
||||
`**Contenu :** ${folder.documentsCount} documents analysés (${folder.size})\n` +
|
||||
`**Thématiques principales :** ${folder.tags.join(", ")}\n` +
|
||||
`**Niveau d'activité :** ${folder.activity.length > 2 ? "Élevé" : "Modéré"} (dernière modification ${formatDate(folder.modified)})\n\n` +
|
||||
`**Recommandations :**\n` +
|
||||
`• ${folder.storageType === "temporary" ? "Considérer l'archivage vers le stockage permanent" : "Dossier déjà archivé de manière optimale"}\n` +
|
||||
`• ${folder.access === "private" ? "Évaluer les possibilités de partage avec l'équipe" : "Partage actuel avec " + folder.members.length + " membre(s)"}\n` +
|
||||
`• Dernière activité significative détectée il y a ${Math.floor(Math.random() * 7) + 1} jour(s)\n\n` +
|
||||
`**Score de pertinence :** ${Math.floor(Math.random() * 30) + 70}/100`,
|
||||
|
||||
`🔍 **Analyse approfondie du dossier "${folder.name}"**\n\n` +
|
||||
`**Structure documentaire :**\n` +
|
||||
`• ${Math.floor(folder.documentsCount * 0.4)} documents principaux\n` +
|
||||
`• ${Math.floor(folder.documentsCount * 0.3)} documents de support\n` +
|
||||
`• ${Math.floor(folder.documentsCount * 0.3)} documents annexes\n\n` +
|
||||
`**Analyse temporelle :**\n` +
|
||||
`• Création : ${folder.created.toLocaleDateString("fr-FR")}\n` +
|
||||
`• Pic d'activité détecté en ${new Date().toLocaleDateString("fr-FR", { month: "long", year: "numeric" })}\n` +
|
||||
`• Tendance : ${Math.random() > 0.5 ? "Croissante" : "Stable"}\n\n` +
|
||||
`**Recommandations stratégiques :**\n` +
|
||||
`• ${folder.documentsCount > 50 ? "Envisager une réorganisation en sous-dossiers" : "Structure actuelle optimale"}\n` +
|
||||
`• ${folder.members.length < 3 ? "Potentiel de collaboration à explorer" : "Équipe collaborative active"}\n` +
|
||||
`• Prochaine révision recommandée : ${new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toLocaleDateString("fr-FR")}`,
|
||||
|
||||
`🎯 **Insights IA pour "${folder.name}"**\n\n` +
|
||||
`**Analyse sémantique :**\n` +
|
||||
`• Cohérence thématique : ${Math.floor(Math.random() * 20) + 80}%\n` +
|
||||
`• Mots-clés dominants : ${folder.tags.slice(0, 3).join(", ")}\n` +
|
||||
`• Complexité moyenne : ${["Faible", "Modérée", "Élevée"][Math.floor(Math.random() * 3)]}\n\n` +
|
||||
`**Patterns détectés :**\n` +
|
||||
`• ${Math.random() > 0.5 ? "Cycle de révision régulier identifié" : "Activité sporadique détectée"}\n` +
|
||||
`• ${Math.random() > 0.5 ? "Collaboration inter-équipes active" : "Usage principalement individuel"}\n` +
|
||||
`• ${folder.storageType === "permanent" ? "Archivage conforme aux bonnes pratiques" : "Optimisation de stockage possible"}\n\n` +
|
||||
`**Actions suggérées :**\n` +
|
||||
`• ${Math.random() > 0.5 ? "Créer un template basé sur ce dossier" : "Standardiser la nomenclature"}\n` +
|
||||
`• ${Math.random() > 0.5 ? "Planifier une session de nettoyage" : "Maintenir la structure actuelle"}\n` +
|
||||
`• Prochaine analyse automatique : ${new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toLocaleDateString("fr-FR")}`,
|
||||
]
|
||||
|
||||
const randomAnalysis = analysisResults[Math.floor(Math.random() * analysisResults.length)]
|
||||
|
||||
// Envoyer l'analyse dans le chat du dossier
|
||||
sendFolderChatNotification(folder.id.toString(), `🤖 ${randomAnalysis}`, "ai_analysis")
|
||||
|
||||
showNotification("success", `Analyse IA terminée pour ${folder.name}. Redirection vers le chat...`)
|
||||
|
||||
// Rediriger vers le chat après 1.5 secondes
|
||||
setTimeout(() => {
|
||||
router.push("/dashboard/chat")
|
||||
}, 1500)
|
||||
},
|
||||
2000 + Math.random() * 3000,
|
||||
)
|
||||
}
|
||||
|
||||
const handleViewCertificate = (folder: FolderData) => {
|
||||
setActionModal({ type: "certificate", folder, folders: [] })
|
||||
}
|
||||
|
||||
const handleViewDocumentsCertificates = (folder: FolderData) => {
|
||||
setActionModal({ type: "documents_certificates", folder, folders: [] })
|
||||
}
|
||||
|
||||
const handleDownloadCertificate = (folder: FolderData) => {
|
||||
if (folder.status === "validated") {
|
||||
showNotification("info", `Téléchargement du certificat blockchain pour le dossier ${folder.name}...`)
|
||||
|
||||
sendFolderChatNotification(
|
||||
folder.id.toString(),
|
||||
`🔗 Certificat blockchain du dossier téléchargé`,
|
||||
"folder_blockchain_certificate_download",
|
||||
)
|
||||
|
||||
setTimeout(() => {
|
||||
showNotification("success", `Certificat blockchain du dossier ${folder.name} téléchargé avec succès`)
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
const handleManageRoles = (folder: FolderData) => {
|
||||
// Rediriger vers la gestion des rôles du dossier
|
||||
router.push(`/dashboard/folders/${folder.id}/roles`)
|
||||
}
|
||||
|
||||
const handleRequestDocument = (folder: FolderData) => {
|
||||
setSelectedDocument("")
|
||||
setRequestMessage("")
|
||||
setActionModal({ type: "request_document", folder, folders: [] })
|
||||
}
|
||||
|
||||
const handleDeleteFolder = (folder: FolderData) => {
|
||||
setActionModal({ type: "delete", folder, folders: [] })
|
||||
}
|
||||
|
||||
const handleOpenModal = (type: FolderType) => {
|
||||
setFolderType(type);
|
||||
setIsModalOpen(true);
|
||||
setMenuOpen(false);
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsModalOpen(false);
|
||||
setFolderType(null);
|
||||
};
|
||||
|
||||
const handleSaveNewFolder = useCallback(
|
||||
(folderData: SDKFolderData) => {
|
||||
if (!isConnected || !userPairingId) {
|
||||
console.error('Conditions non remplies:', { isConnected, userPairingId });
|
||||
showNotification(
|
||||
"error",
|
||||
`Vous devez être connecté à 4NK pour créer un dossier (Connected: ${isConnected}, PairingId: ${userPairingId ? 'OK' : 'NULL'})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ajout du type dans les données du dossier
|
||||
const folderToCreate = {
|
||||
...folderData,
|
||||
type: folderType
|
||||
};
|
||||
|
||||
const roles = setDefaultFolderRoles(userPairingId, [], []);
|
||||
const folderPrivateFields = FolderPrivateFields;
|
||||
|
||||
MessageBus.getInstance(iframeUrl)
|
||||
.createFolder(folderToCreate, folderPrivateFields, roles)
|
||||
.then((_folderCreated: FolderCreated) => {
|
||||
const firstStateId = _folderCreated.process.states[0].state_id;
|
||||
MessageBus.getInstance(iframeUrl)
|
||||
.notifyProcessUpdate(_folderCreated.processId, firstStateId)
|
||||
.then(() =>
|
||||
MessageBus.getInstance(iframeUrl)
|
||||
.validateState(_folderCreated.processId, firstStateId)
|
||||
.then(() =>
|
||||
MessageBus.getInstance(iframeUrl)
|
||||
.getProcesses()
|
||||
.then(async (processes: any) => {
|
||||
setProcesses(processes)
|
||||
setFolderProcesses(processes) // Update folder processes as well
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
setShowCreateFolderModal(false);
|
||||
showNotification("success", `Dossier "${folderData.name}" créé avec succès sur 4NK`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Erreur lors de la création du dossier 4NK:', error);
|
||||
showNotification("error", "Erreur lors de la création du dossier");
|
||||
});
|
||||
},
|
||||
[userPairingId, isConnected, iframeUrl, folderType]
|
||||
);
|
||||
|
||||
const handleToggleFavorite = (folderId: number) => {
|
||||
const folder = folders.find((f) => f.id === folderId)
|
||||
if (!folder) return
|
||||
|
||||
setFolders((prev) => prev.map((f) => (f.id === folderId ? { ...f, favorite: !f.favorite } : f)))
|
||||
|
||||
const action = folder.favorite ? "retiré des" : "ajouté aux"
|
||||
showNotification("success", `${folder.name} ${action} favoris`)
|
||||
|
||||
sendFolderChatNotification(folderId.toString(), `⭐ Le dossier a été ${action} favoris`, "favorite")
|
||||
}
|
||||
|
||||
// Bulk actions
|
||||
const handleBulkDownload = () => {
|
||||
const selectedFolderData = folders.filter((folder) => selectedFolders.includes(folder.id))
|
||||
showNotification("info", `Téléchargement de ${selectedFolderData.length} dossier(s)...`)
|
||||
|
||||
selectedFolderData.forEach((folder) => {
|
||||
sendFolderChatNotification(folder.id.toString(), `📥 Le dossier a été téléchargé`, "download")
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
showNotification("success", `${selectedFolderData.length} dossier(s) téléchargé(s) avec succès`)
|
||||
setSelectedFolders([])
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const handleBulkInvite = () => {
|
||||
const selectedFolderData = folders.filter(
|
||||
(folder) => selectedFolders.includes(folder.id) && folder.permissions.canInvite,
|
||||
)
|
||||
if (selectedFolderData.length === 0) {
|
||||
showNotification("error", "Aucun dossier sélectionné ne peut être partagé")
|
||||
return
|
||||
}
|
||||
setInviteMessage("")
|
||||
setSelectedUser("")
|
||||
setSelectedRole("")
|
||||
setInviteScope("user")
|
||||
setActionModal({ type: "invite", folder: null, folders: selectedFolderData })
|
||||
}
|
||||
|
||||
const handleBulkArchive = () => {
|
||||
const selectedFolderData = folders.filter(
|
||||
(folder) => selectedFolders.includes(folder.id) && folder.permissions.canArchive,
|
||||
)
|
||||
if (selectedFolderData.length === 0) {
|
||||
showNotification("error", "Aucun dossier sélectionné ne peut être archivé")
|
||||
return
|
||||
}
|
||||
setArchiveReason("")
|
||||
setRetentionPeriod("5")
|
||||
setActionModal({ type: "archive", folder: null, folders: selectedFolderData })
|
||||
}
|
||||
|
||||
const handleBulkAIAnalysis = () => {
|
||||
const selectedFolderData = folders.filter(
|
||||
(folder) => selectedFolders.includes(folder.id) && folder.permissions.canAnalyze,
|
||||
)
|
||||
if (selectedFolderData.length === 0) {
|
||||
showNotification("error", "Aucun dossier sélectionné ne peut être analysé")
|
||||
return
|
||||
}
|
||||
|
||||
showNotification("info", `Analyse IA en cours pour ${selectedFolderData.length} dossier(s)...`)
|
||||
|
||||
// Analyser chaque dossier avec un délai échelonné
|
||||
selectedFolderData.forEach((folder, index) => {
|
||||
setTimeout(() => {
|
||||
const bulkAnalysis =
|
||||
`📊 **Analyse IA groupée - Dossier "${folder.name}"**\n\n` +
|
||||
`**Position dans l'analyse :** ${index + 1}/${selectedFolderData.length}\n` +
|
||||
`**Contenu :** ${folder.documentsCount} documents (${folder.size})\n` +
|
||||
`**Tags :** ${folder.tags.join(", ")}\n\n` +
|
||||
`**Analyse comparative :**\n` +
|
||||
`• Taille relative : ${folder.documentsCount > 40 ? "Au-dessus de la moyenne" : "Dans la moyenne"}\n` +
|
||||
`• Activité : ${folder.activity.length > 1 ? "Active" : "Modérée"}\n` +
|
||||
`• Collaboration : ${folder.members.length} membre(s)\n\n` +
|
||||
`**Recommandation :** ${folder.storageType === "temporary" ? "Candidat à l'archivage" : "Archivage optimal"}\n` +
|
||||
`**Score global :** ${Math.floor(Math.random() * 30) + 70}/100`
|
||||
|
||||
sendFolderChatNotification(folder.id.toString(), `🤖 ${bulkAnalysis}`, "bulk_ai_analysis")
|
||||
}, index * 1500) // Échelonner les analyses
|
||||
})
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
const totalDocs = selectedFolderData.reduce((sum, folder) => sum + folder.documentsCount, 0)
|
||||
showNotification(
|
||||
"success",
|
||||
`Analyse IA terminée pour ${selectedFolderData.length} dossier(s) (${totalDocs} documents). Redirection vers le chat...`,
|
||||
)
|
||||
setSelectedFolders([])
|
||||
|
||||
// Rediriger vers le chat après l'analyse groupée
|
||||
setTimeout(() => {
|
||||
router.push("/dashboard/chat")
|
||||
}, 1500)
|
||||
},
|
||||
selectedFolderData.length * 1500 + 1000,
|
||||
)
|
||||
}
|
||||
|
||||
const handleBulkDelete = () => {
|
||||
const selectedFolderData = folders.filter(
|
||||
(folder) => selectedFolders.includes(folder.id) && folder.permissions.canDelete,
|
||||
)
|
||||
if (selectedFolderData.length === 0) {
|
||||
showNotification("error", "Aucun dossier sélectionné ne peut être supprimé")
|
||||
return
|
||||
}
|
||||
setActionModal({ type: "delete", folder: null, folders: selectedFolderData })
|
||||
}
|
||||
|
||||
// Modal actions
|
||||
const confirmInvite = () => {
|
||||
const recipient =
|
||||
inviteScope === "user"
|
||||
? users.find((u) => u.id === selectedUser)?.name
|
||||
: roles.find((r) => r.id === selectedRole)?.name
|
||||
|
||||
if (actionModal.folder) {
|
||||
showNotification("success", `${actionModal.folder.name} partagé avec ${recipient}. Un message a été envoyé.`)
|
||||
sendFolderChatNotification(
|
||||
actionModal.folder.id.toString(),
|
||||
`👥 Le dossier a été partagé avec ${recipient}. Message: ${inviteMessage}`,
|
||||
"invite",
|
||||
)
|
||||
} else if (actionModal.folders.length > 0) {
|
||||
actionModal.folders.forEach((folder) => {
|
||||
sendFolderChatNotification(
|
||||
folder.id.toString(),
|
||||
`👥 Le dossier a été partagé avec ${recipient}. Message: ${inviteMessage}`,
|
||||
"bulk_invite",
|
||||
)
|
||||
})
|
||||
showNotification(
|
||||
"success",
|
||||
`${actionModal.folders.length} dossier(s) partagé(s) avec ${recipient}. Messages envoyés.`,
|
||||
)
|
||||
setSelectedFolders([])
|
||||
}
|
||||
setActionModal({ type: null, folder: null, folders: [] })
|
||||
}
|
||||
|
||||
const confirmRequestDocument = () => {
|
||||
if (actionModal.folder && selectedDocument) {
|
||||
const document = actionModal.folder.expectedDocuments.find((doc) => doc.name === selectedDocument)
|
||||
if (document) {
|
||||
// Trouver l'utilisateur avec le rôle assigné
|
||||
const assignedUser = users.find(
|
||||
(user) => user.folderRoles[actionModal.folder!.id.toString()]?.role === document.assignedRole,
|
||||
)
|
||||
|
||||
if (assignedUser) {
|
||||
// Préparer les données pour le chat
|
||||
const messageData = {
|
||||
userName: assignedUser.name,
|
||||
subject: `Demande de document - ${selectedDocument}`,
|
||||
content: `Bonjour ${assignedUser.name},\n\nPouvez-vous fournir le document "${selectedDocument}" pour le dossier "${actionModal.folder.name}" ?\n\n${requestMessage}\n\nMerci !`,
|
||||
}
|
||||
|
||||
// Stocker dans sessionStorage pour le chat
|
||||
sessionStorage.setItem("newMessage", JSON.stringify(messageData))
|
||||
|
||||
showNotification("success", `Demande envoyée à ${assignedUser.name}. Redirection vers le chat...`)
|
||||
|
||||
// Rediriger vers le chat avec l'utilisateur
|
||||
setTimeout(() => {
|
||||
router.push(`/dashboard/chat?user=${assignedUser.id}&message=new`)
|
||||
}, 1500)
|
||||
} else {
|
||||
showNotification("error", "Aucun utilisateur trouvé avec le rôle requis pour ce document")
|
||||
}
|
||||
}
|
||||
}
|
||||
setActionModal({ type: null, folder: null, folders: [] })
|
||||
}
|
||||
|
||||
const confirmStorageConfig = () => {
|
||||
if (actionModal.folder) {
|
||||
const updatedFolder = {
|
||||
...actionModal.folder,
|
||||
temporaryStorageConfig: {
|
||||
duration: Number.parseInt(storageDuration),
|
||||
dataUsage: dataUsage,
|
||||
thirdPartyAccess: thirdPartyAccess,
|
||||
},
|
||||
modified: new Date(),
|
||||
}
|
||||
setFolders((prev) => prev.map((f) => (f.id === updatedFolder.id ? updatedFolder : f)))
|
||||
showNotification("success", `Configuration du stockage temporaire mise à jour pour ${actionModal.folder.name}`)
|
||||
|
||||
// Notification dans le chat du dossier
|
||||
const message = `⚙️ Configuration du stockage temporaire mise à jour :\n• Durée : ${storageDuration} jours\n• Usage : ${dataUsage}\n• Accès tiers : ${thirdPartyAccess}`
|
||||
sendFolderChatNotification(actionModal.folder.id.toString(), message, "storage_config")
|
||||
}
|
||||
setActionModal({ type: null, folder: null, folders: [] })
|
||||
}
|
||||
|
||||
const confirmArchive = () => {
|
||||
if (actionModal.folder) {
|
||||
const updatedFolder = {
|
||||
...actionModal.folder,
|
||||
storageType: "permanent" as const,
|
||||
modified: new Date(),
|
||||
}
|
||||
setFolders((prev) => prev.map((f) => (f.id === updatedFolder.id ? updatedFolder : f)))
|
||||
showNotification(
|
||||
"success",
|
||||
`${actionModal.folder.name} et tous ses documents archivés vers le stockage permanent`,
|
||||
)
|
||||
|
||||
// Notification dans le chat du dossier
|
||||
let message = `📦 Le dossier et tous ses ${actionModal.folder.documentsCount} document(s) ont été archivés vers le stockage permanent (conservation: ${retentionPeriod} ans)`
|
||||
if (archiveReason.trim()) {
|
||||
message += ` - Raison: ${archiveReason}`
|
||||
}
|
||||
|
||||
sendFolderChatNotification(actionModal.folder.id.toString(), message, "archive")
|
||||
} else if (actionModal.folders.length > 0) {
|
||||
const folderIds = actionModal.folders.map((f) => f.id)
|
||||
setFolders((prev) =>
|
||||
prev.map((f) =>
|
||||
folderIds.includes(f.id)
|
||||
? {
|
||||
...f,
|
||||
storageType: "permanent" as const,
|
||||
modified: new Date(),
|
||||
}
|
||||
: f,
|
||||
),
|
||||
)
|
||||
|
||||
actionModal.folders.forEach((folder) => {
|
||||
let message = `📦 Le dossier et tous ses ${folder.documentsCount} document(s) ont été archivés vers le stockage permanent (conservation: ${retentionPeriod} ans)`
|
||||
if (archiveReason.trim()) {
|
||||
message += ` - Raison: ${archiveReason}`
|
||||
}
|
||||
sendFolderChatNotification(folder.id.toString(), message, "bulk_archive")
|
||||
})
|
||||
|
||||
const totalDocuments = actionModal.folders.reduce((sum, folder) => sum + folder.documentsCount, 0)
|
||||
showNotification(
|
||||
"success",
|
||||
`${actionModal.folders.length} dossier(s) et ${totalDocuments} document(s) archivés vers le stockage permanent`,
|
||||
)
|
||||
setSelectedFolders([])
|
||||
}
|
||||
setActionModal({ type: null, folder: null, folders: [] })
|
||||
}
|
||||
|
||||
const confirmCreate = () => {
|
||||
const newFolder: FolderData = {
|
||||
id: Math.max(...folders.map((f) => f.id)) + 1,
|
||||
name: folderName,
|
||||
description: folderDescription,
|
||||
documentsCount: 0,
|
||||
subfoldersCount: 0,
|
||||
size: "0 MB",
|
||||
created: new Date(),
|
||||
modified: new Date(),
|
||||
owner: "Utilisateur actuel",
|
||||
access: folderAccess,
|
||||
members: ["Utilisateur actuel"],
|
||||
tags: folderTags
|
||||
.split(",")
|
||||
.map((tag) => tag.trim())
|
||||
.filter((tag) => tag),
|
||||
color: folderColor,
|
||||
favorite: false,
|
||||
storageType: "temporary",
|
||||
status: "active",
|
||||
type: "general",
|
||||
expectedDocuments: [],
|
||||
activity: [],
|
||||
permissions: {
|
||||
canView: true,
|
||||
canEdit: true,
|
||||
canDelete: true,
|
||||
canInvite: true,
|
||||
canArchive: true,
|
||||
canAnalyze: true,
|
||||
},
|
||||
documents: [],
|
||||
}
|
||||
setFolders((prev) => [...prev, newFolder])
|
||||
showNotification("success", `Dossier "${folderName}" créé avec succès`)
|
||||
setActionModal({ type: null, folder: null, folders: [] })
|
||||
}
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (actionModal.folder) {
|
||||
sendFolderChatNotification(actionModal.folder.id.toString(), `🗑️ Le dossier a été supprimé`, "delete")
|
||||
setFolders((prev) => prev.filter((f) => f.id !== actionModal.folder!.id))
|
||||
showNotification("success", `${actionModal.folder.name} supprimé`)
|
||||
} else if (actionModal.folders.length > 0) {
|
||||
actionModal.folders.forEach((folder) => {
|
||||
sendFolderChatNotification(folder.id.toString(), `🗑️ Le dossier a été supprimé`, "bulk_delete")
|
||||
})
|
||||
const folderIds = actionModal.folders.map((f) => f.id)
|
||||
setFolders((prev) => prev.filter((f) => !folderIds.includes(f.id)))
|
||||
showNotification("success", `${actionModal.folders.length} dossier(s) supprimé(s)`)
|
||||
setSelectedFolders([])
|
||||
}
|
||||
setActionModal({ type: null, folder: null, folders: [] })
|
||||
}
|
||||
|
||||
const typeFilter = searchParams.get("type")
|
||||
|
||||
const filteredFolders = folders
|
||||
.filter((folder) => {
|
||||
if (searchTerm && !folder.name.toLowerCase().includes(searchTerm.toLowerCase())) {
|
||||
return false
|
||||
}
|
||||
if (typeFilter && folder.type !== typeFilter) {
|
||||
return false
|
||||
}
|
||||
if (filterAccess !== "all" && folder.access !== filterAccess) {
|
||||
return false
|
||||
}
|
||||
if (filterOwner !== "all" && folder.owner !== filterOwner) {
|
||||
return false
|
||||
}
|
||||
if (filterStorage !== "all" && folder.storageType !== filterStorage) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
.sort((a, b) => {
|
||||
let aValue, bValue
|
||||
switch (sortBy) {
|
||||
case "name":
|
||||
aValue = a.name.toLowerCase()
|
||||
bValue = b.name.toLowerCase()
|
||||
break
|
||||
case "size":
|
||||
aValue = Number.parseFloat(a.size.replace(/[^\d.]/g, ""))
|
||||
bValue = Number.parseFloat(b.size.replace(/[^\d.]/g, ""))
|
||||
break
|
||||
case "owner":
|
||||
aValue = a.owner.toLowerCase()
|
||||
bValue = b.owner.toLowerCase()
|
||||
break
|
||||
case "documents":
|
||||
aValue = a.documentsCount
|
||||
bValue = b.documentsCount
|
||||
break
|
||||
case "modified":
|
||||
default:
|
||||
aValue = a.modified.getTime()
|
||||
bValue = b.modified.getTime()
|
||||
break
|
||||
}
|
||||
|
||||
if (sortOrder === "asc") {
|
||||
return aValue > bValue ? 1 : -1
|
||||
} else {
|
||||
return aValue < bValue ? 1 : -1
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const getFolderColor = (color: string) => {
|
||||
const colorObj = colors.find((c) => c.id === color)
|
||||
return colorObj?.class || "text-gray-600 bg-gray-100"
|
||||
}
|
||||
|
||||
const isNewFolder = (folder: FolderData) => {
|
||||
const now = Date.now()
|
||||
const diffMs = now - folder.modified.getTime()
|
||||
const twoDaysMs = 2 * 24 * 60 * 60 * 1000
|
||||
return diffMs <= twoDaysMs
|
||||
}
|
||||
|
||||
const getStorageIcon = (storageType: string) => {
|
||||
return storageType === "permanent" ? (
|
||||
<Cloud className="h-4 w-4 text-blue-600" />
|
||||
) : (
|
||||
<HardDrive className="h-4 w-4 text-gray-600" />
|
||||
)
|
||||
}
|
||||
|
||||
const getRoleIcon = (role: string) => {
|
||||
switch (role) {
|
||||
case "owner":
|
||||
return <Crown className="h-4 w-4 text-yellow-600" />
|
||||
case "editor":
|
||||
return <FileText className="h-4 w-4 text-blue-600" />
|
||||
case "validator":
|
||||
return <Shield className="h-4 w-4 text-green-600" />
|
||||
case "contributor":
|
||||
return <UserPlus className="h-4 w-4 text-purple-600" />
|
||||
case "viewer":
|
||||
return <FileText className="h-4 w-4 text-gray-600" />
|
||||
case "admin":
|
||||
return <Shield className="h-4 w-4 text-red-600" />
|
||||
case "manager":
|
||||
return <Users className="h-4 w-4 text-orange-600" />
|
||||
case "user":
|
||||
return <User className="h-4 w-4 text-blue-600" />
|
||||
case "guest":
|
||||
return <User className="h-4 w-4 text-gray-400" />
|
||||
default:
|
||||
return <User className="h-4 w-4 text-gray-600" />
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case "active":
|
||||
return <Badge className="bg-green-100 text-green-800 border-green-200">Actif</Badge>
|
||||
case "pending":
|
||||
return <Badge className="bg-orange-100 text-orange-800 border-orange-200">En attente</Badge>
|
||||
case "completed":
|
||||
return <Badge className="bg-blue-100 text-blue-800 border-blue-200">Terminé</Badge>
|
||||
case "archived":
|
||||
return <Badge className="bg-gray-100 text-gray-800 border-gray-200">Archivé</Badge>
|
||||
case "validated":
|
||||
return <Badge className="bg-green-300 text-green-800 border-green-400">Validé</Badge>
|
||||
default:
|
||||
return <Badge className="bg-gray-100 text-gray-800 border-gray-200">Inconnu</Badge>
|
||||
}
|
||||
}
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
const now = new Date()
|
||||
const diffInHours = (now.getTime() - date.getTime()) / (1000 * 60 * 60)
|
||||
|
||||
if (diffInHours < 1) {
|
||||
return "Il y a quelques minutes"
|
||||
} else if (diffInHours < 24) {
|
||||
return `Il y a ${Math.floor(diffInHours)} heure${Math.floor(diffInHours) > 1 ? "s" : ""}`
|
||||
} else if (diffInHours < 48) {
|
||||
return "Hier"
|
||||
} else {
|
||||
return date.toLocaleDateString("fr-FR")
|
||||
}
|
||||
}
|
||||
|
||||
const toggleFolderSelection = (folderId: number) => {
|
||||
setSelectedFolders((prev) => (prev.includes(folderId) ? prev.filter((id) => id !== folderId) : [...prev, folderId]))
|
||||
}
|
||||
|
||||
const selectAllFolders = () => {
|
||||
if (selectedFolders.length === filteredFolders.length) {
|
||||
setSelectedFolders([])
|
||||
} else {
|
||||
setSelectedFolders(filteredFolders.map((folder) => folder.id))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Notification */}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user