Agent toggle switches in ProjectSettings

Agents shown as toggle switches (on/off) instead of dropdown + remove button.
Humans listed separately without remove option.
Toggle calls addProjectMember/removeProjectMember API.
This commit is contained in:
Markov 2026-02-27 22:57:48 +01:00
parent 82c2ff4c53
commit bd583384d1

View File

@ -75,14 +75,7 @@ export default function ProjectSettings({ project, onUpdated }: Props) {
} }
}; };
const handleRemoveMember = async (memberId: string) => { // handleRemoveMember is now inline in agent toggle
try {
await removeProjectMember(project.id, memberId);
setMembers(members.filter((m) => m.id !== memberId));
} catch (e) {
console.error("Failed to remove member:", e);
}
};
const handleSave = async () => { const handleSave = async () => {
setSaving(true); setSaving(true);
@ -180,43 +173,76 @@ export default function ProjectSettings({ project, onUpdated }: Props) {
<div className="text-sm text-[var(--muted)]">Загрузка...</div> <div className="text-sm text-[var(--muted)]">Загрузка...</div>
) : ( ) : (
<div className="space-y-3"> <div className="space-y-3">
{/* Current Members */} {/* Humans (non-removable except by dropdown) */}
<div className="space-y-2"> <div className="space-y-2">
{members.map((member) => ( {members.filter(m => m.type !== "agent").map((member) => (
<div key={member.slug} className="flex items-center justify-between p-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg"> <div key={member.slug} className="flex items-center justify-between p-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium ${ <div className="w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium bg-green-500/20 text-green-400">
member.type === "agent" ? "bg-blue-500/20 text-blue-400" : "bg-green-500/20 text-green-400" H
}`}>
{member.type === "agent" ? "A" : "H"}
</div> </div>
<div> <div>
<div className="text-sm font-medium">{member.name}</div> <div className="text-sm font-medium">{member.name}</div>
<div className="text-xs text-[var(--muted)]">{member.slug} {member.role}</div> <div className="text-xs text-[var(--muted)]">{member.slug} {member.role}</div>
</div> </div>
</div> </div>
{member.role !== "owner" && (
<button
onClick={() => handleRemoveMember(member.id)}
className="px-2 py-1 text-xs text-red-400 hover:bg-red-500/10 rounded"
>
Убрать
</button>
)}
</div> </div>
))} ))}
</div> </div>
{/* Add Member */} {/* Agents with toggle switches */}
{availableMembers.length > 0 && ( <div className="text-xs text-[var(--muted)] mt-4 mb-2">Агенты</div>
<div className="flex gap-2"> <div className="space-y-2">
{allMembers.filter(m => m.type === "agent").map((agent) => {
const isActive = members.some(pm => pm.id === agent.id);
return (
<div key={agent.id} className="flex items-center justify-between p-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg">
<div className="flex items-center gap-3">
<div className="w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium bg-blue-500/20 text-blue-400">
A
</div>
<div>
<div className="text-sm font-medium">{agent.name}</div>
<div className="text-xs text-[var(--muted)]">{agent.slug}</div>
</div>
</div>
<button
onClick={async () => {
try {
if (isActive) {
await removeProjectMember(project.id, agent.id);
setMembers(members.filter(m => m.id !== agent.id));
} else {
await addProjectMember(project.id, agent.id);
setMembers([...members, { id: agent.id, name: agent.name, slug: agent.slug, type: agent.type, role: "member" } as ProjectMember]);
}
} catch (e) {
console.error("Failed to toggle agent:", e);
}
}}
className={`relative w-10 h-5 rounded-full transition-colors duration-200 ${
isActive ? "bg-[var(--accent)]" : "bg-[var(--border)]"
}`}
>
<span className={`absolute top-0.5 left-0.5 w-4 h-4 rounded-full bg-white transition-transform duration-200 ${
isActive ? "translate-x-5" : "translate-x-0"
}`} />
</button>
</div>
);
})}
</div>
{/* Add human member */}
{availableMembers.filter(m => m.type !== "agent").length > 0 && (
<div className="flex gap-2 mt-3">
<select <select
value={selectedMember} value={selectedMember}
onChange={(e) => setSelectedMember(e.target.value)} onChange={(e) => setSelectedMember(e.target.value)}
className="flex-1 px-3 py-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg outline-none focus:border-[var(--accent)] text-sm" className="flex-1 px-3 py-2 bg-[var(--bg)] border border-[var(--border)] rounded-lg outline-none focus:border-[var(--accent)] text-sm"
> >
<option value="">Добавить участника...</option> <option value="">Добавить участника...</option>
{availableMembers.map((member) => ( {availableMembers.filter(m => m.type !== "agent").map((member) => (
<option key={member.id} value={member.id}> <option key={member.id} value={member.id}>
{member.name} ({member.slug}) - {member.type} {member.name} ({member.slug}) - {member.type}
</option> </option>