Skip to main content
Glama

Collective Brain MCP Server

by bparpette
page.tsx12.5 kB
'use client' import { useState, useEffect } from 'react' import { Team, User } from '@/lib/supabase' export default function Home() { const [teams, setTeams] = useState<Team[]>([]) const [selectedTeam, setSelectedTeam] = useState<Team | null>(null) const [users, setUsers] = useState<User[]>([]) const [loading, setLoading] = useState(true) const [newTeamName, setNewTeamName] = useState('') const [newUserEmail, setNewUserEmail] = useState('') const [newUserName, setNewUserName] = useState('') // Charger les équipes au démarrage useEffect(() => { loadTeams() }, []) // Charger les utilisateurs quand une équipe est sélectionnée useEffect(() => { if (selectedTeam) { loadUsers(selectedTeam.id) } }, [selectedTeam]) const loadTeams = async () => { try { const response = await fetch('/api/teams') const data = await response.json() setTeams(data.teams || []) } catch (error) { console.error('Erreur lors du chargement des équipes:', error) } finally { setLoading(false) } } const loadUsers = async (teamId: string) => { try { const response = await fetch(`/api/teams/${teamId}/users`) const data = await response.json() setUsers(data.users || []) } catch (error) { console.error('Erreur lors du chargement des utilisateurs:', error) } } const createTeam = async () => { if (!newTeamName.trim()) return try { const response = await fetch('/api/teams', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: newTeamName }) }) if (response.ok) { setNewTeamName('') loadTeams() } else { const error = await response.json() alert(`Erreur: ${error.error}`) } } catch (error) { console.error('Erreur lors de la création de l\'équipe:', error) } } const addUser = async () => { if (!selectedTeam || !newUserEmail.trim() || !newUserName.trim()) return try { const response = await fetch(`/api/teams/${selectedTeam.id}/users`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: newUserEmail, name: newUserName }) }) if (response.ok) { setNewUserEmail('') setNewUserName('') loadUsers(selectedTeam.id) } else { const error = await response.json() alert(`Erreur: ${error.error}`) } } catch (error) { console.error('Erreur lors de l\'ajout de l\'utilisateur:', error) } } const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text) alert('Token copié dans le presse-papiers!') } if (loading) { return ( <div className="min-h-screen bg-gray-50 flex items-center justify-center"> <div className="text-xl">Chargement...</div> </div> ) } return ( <div className="min-h-screen bg-white"> <div className="max-w-6xl mx-auto py-8 px-4"> <div className="text-center mb-12"> <h1 className="text-3xl font-bold text-gray-900 mb-2"> Collective Brain </h1> <p className="text-lg text-gray-600"> Gestion des équipes - Mémoire collective multi-tenant </p> </div> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> {/* Section Équipes */} <div className="bg-white border border-gray-200 rounded-lg shadow-sm p-6"> <h2 className="text-xl font-semibold text-gray-900 mb-6"> Équipes </h2> {/* Créer une nouvelle équipe */} <div className="mb-6 p-4 bg-gray-50 rounded-lg border border-gray-200"> <h3 className="font-medium text-gray-900 mb-3">Créer une nouvelle équipe</h3> <div className="flex gap-2"> <input type="text" placeholder="Nom de l'équipe" value={newTeamName} onChange={(e) => setNewTeamName(e.target.value)} className="flex-1 px-3 py-2 border border-gray-300 rounded-md text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" /> <button onClick={createTeam} className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors" > Créer </button> </div> </div> {/* Liste des équipes */} <div className="space-y-3"> {teams.map((team) => ( <div key={team.id} className={`p-4 border rounded-lg cursor-pointer transition-colors ${ selectedTeam?.id === team.id ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300 hover:bg-gray-50' }`} onClick={() => setSelectedTeam(team)} > <div className="font-medium text-gray-900 mb-2">{team.name}</div> <div className="text-sm text-gray-600 flex items-center"> <span className="font-mono bg-gray-100 px-2 py-1 rounded text-xs border"> {team.team_token} </span> <button onClick={(e) => { e.stopPropagation() copyToClipboard(team.team_token) }} className="ml-2 p-1 hover:bg-gray-200 rounded transition-colors" title="Copier le token" > <svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> </svg> </button> </div> </div> ))} </div> </div> {/* Section Utilisateurs */} <div className="bg-white border border-gray-200 rounded-lg shadow-sm p-6"> <h2 className="text-xl font-semibold text-gray-900 mb-6"> Utilisateurs {selectedTeam ? `- ${selectedTeam.name}` : ''} </h2> {selectedTeam ? ( <> {/* Ajouter un utilisateur */} <div className="mb-6 p-4 bg-gray-50 rounded-lg border border-gray-200"> <h3 className="font-medium text-gray-900 mb-3">Ajouter un utilisateur</h3> <div className="space-y-3"> <input type="email" placeholder="Email" value={newUserEmail} onChange={(e) => setNewUserEmail(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500" /> <input type="text" placeholder="Nom" value={newUserName} onChange={(e) => setNewUserName(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500" /> <button onClick={addUser} className="w-full px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 transition-colors" > Ajouter </button> </div> </div> {/* Liste des utilisateurs */} <div className="space-y-3"> {users.map((user) => ( <div key={user.id} className="p-4 border border-gray-200 rounded-lg bg-gray-50"> <div className="flex justify-between items-start"> <div className="flex-1"> <div className="font-medium text-gray-900 mb-1">{user.name}</div> <div className="text-sm text-gray-600 mb-2">{user.email}</div> <div className="text-sm text-gray-600 flex items-center"> <span className="font-mono bg-gray-100 px-2 py-1 rounded text-xs border"> {user.user_token} </span> <button onClick={() => copyToClipboard(user.user_token)} className="ml-2 p-1 hover:bg-gray-200 rounded transition-colors" title="Copier le token" > <svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> </svg> </button> </div> </div> <span className={`px-2 py-1 text-xs rounded-full font-medium ${ user.role === 'admin' ? 'bg-red-100 text-red-800 border border-red-200' : 'bg-gray-100 text-gray-800 border border-gray-200' }`}> {user.role === 'admin' ? 'Admin' : 'Membre'} </span> </div> </div> ))} </div> </> ) : ( <div className="text-gray-500 text-center py-12"> <div className="text-4xl mb-4">👆</div> <p className="text-lg">Sélectionnez une équipe pour voir ses utilisateurs</p> </div> )} </div> </div> {/* Instructions */} <div className="mt-8 bg-gray-50 border border-gray-200 rounded-lg p-6"> <h3 className="font-semibold text-gray-900 mb-4 text-lg">Instructions d'utilisation</h3> <div className="text-gray-700 space-y-3"> <div className="flex items-start"> <span className="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-medium mr-3 mt-0.5">1</span> <p><strong className="text-gray-900">Créez une équipe</strong> et copiez son token d'équipe</p> </div> <div className="flex items-start"> <span className="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-medium mr-3 mt-0.5">2</span> <p><strong className="text-gray-900">Ajoutez des utilisateurs</strong> à l'équipe et copiez leurs tokens utilisateur</p> </div> <div className="flex items-start"> <span className="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-medium mr-3 mt-0.5">3</span> <p><strong className="text-gray-900">Dans Le Chat</strong>, utilisez le token utilisateur pour accéder à la mémoire collective de l'équipe</p> </div> <div className="flex items-start"> <span className="bg-blue-600 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-medium mr-3 mt-0.5">4</span> <p><strong className="text-gray-900">Chaque équipe</strong> a sa propre collection de mémoires isolée</p> </div> </div> </div> </div> </div> ) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bparpette/MistralHackathonMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server