diff --git a/src/components/admin/UserManagementPanel.jsx b/src/components/admin/UserManagementPanel.jsx index f671937..f2ccdfa 100644 --- a/src/components/admin/UserManagementPanel.jsx +++ b/src/components/admin/UserManagementPanel.jsx @@ -3,7 +3,7 @@ import { Panel } from '../UI/Panel'; import { Badge } from '../UI/Badge'; import { Input } from '../UI/Input'; -import { supabase } from '../../supabaseClient'; +import { supabase, supabaseUrl } from '../../supabaseClient'; const ROLES = ['admin', 'driver', 'logistician', 'manager', 'mega_admin']; @@ -23,19 +23,45 @@ const ROLE_TONES = { driver: 'accent', }; +/* ── Call manage-users edge function ── */ +async function adminApi(method, body) { + const { data: { session } } = await supabase.auth.getSession(); + const token = session?.access_token; + if (!token) throw new Error('Не авторизован'); + + const opts = { + method, + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}`, + 'apikey': import.meta.env.VITE_SUPABASE_ANON_KEY, + }, + }; + if (body && method !== 'DELETE') opts.body = JSON.stringify(body); + + const url = method === 'DELETE' && body?.id + ? `${supabaseUrl}/functions/v1/manage-users?id=${body.id}` + : `${supabaseUrl}/functions/v1/manage-users`; + + const res = await fetch(url, opts); + const json = await res.json(); + if (!res.ok) throw new Error(json.error || `Ошибка ${res.status}`); + return json; +} + /* ── Custom dropdown (matches app design system) ── */ -function RoleDropdown({ value, onChange, onClose }) { +function RoleDropdown({ value, onChange }) { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { if (!open) return; - const onDown = (e) => { if (ref.current && !ref.current.contains(e.target)) { setOpen(false); onClose?.(); } }; - const onKey = (e) => { if (e.key === 'Escape') { setOpen(false); onClose?.(); } }; + const onDown = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; + const onKey = (e) => { if (e.key === 'Escape') setOpen(false); }; document.addEventListener('pointerdown', onDown); document.addEventListener('keydown', onKey); return () => { document.removeEventListener('pointerdown', onDown); document.removeEventListener('keydown', onKey); }; - }, [open, onClose]); + }, [open]); const pick = (role) => { onChange(role); setOpen(false); }; @@ -50,7 +76,7 @@ function RoleDropdown({ value, onChange, onClose }) { {open && ( -
+
{ROLES.map((r) => { const sel = r === value; return ( @@ -59,7 +85,7 @@ function RoleDropdown({ value, onChange, onClose }) { type="button" onClick={() => pick(r)} className={[ - 'flex w-full items-center justify-between px-4 py-3 text-left text-sm transition', + 'flex w-full items-center justify-between px-4 py-2.5 text-left text-sm transition', sel ? 'bg-[var(--color-accent-soft)] text-[var(--color-text)]' : 'text-[var(--color-text)] hover:bg-[var(--color-surface-strong)]', ].join(' ')} > @@ -119,24 +145,14 @@ export default function UserManagementPanel() { return match ? match.id : null; }; - /* ── Add ── */ + /* ── Add via edge function ── */ const handleAddUser = async (e) => { e.preventDefault(); setAddError(null); if (!addForm.name || !addForm.email || !addForm.role) { setAddError('Все поля обязательны.'); return; } setAddSubmitting(true); try { - const roleId = getRoleId(addForm.role); - const { data: authData, error: authErr } = await supabase.auth.admin.createUser({ - email: addForm.email, - email_confirm: true, - user_metadata: { name: addForm.name }, - }); - if (authErr) throw authErr; - const { error: insertErr } = await supabase - .from('users') - .insert({ id: authData.user.id, email: addForm.email, name: addForm.name, role_id: roleId }); - if (insertErr) throw insertErr; + await adminApi('POST', { email: addForm.email, name: addForm.name, role: addForm.role }); await fetchUsers(); setShowAddForm(false); setAddForm({ name: '', email: '', role: '' }); @@ -145,7 +161,7 @@ export default function UserManagementPanel() { } finally { setAddSubmitting(false); } }; - /* ── Edit ── */ + /* ── Edit via edge function ── */ const startEdit = (user) => { setEditingId(user.id); setEditForm({ name: user.name || '', email: user.email || '', role: getRoleName(user) }); @@ -155,24 +171,17 @@ export default function UserManagementPanel() { const saveEdit = async () => { setSaving(true); try { - const roleId = getRoleId(editForm.role); - const { error: err } = await supabase - .from('users') - .update({ name: editForm.name, email: editForm.email, role_id: roleId }) - .eq('id', editingId); - if (err) throw err; + await adminApi('PATCH', { id: editingId, name: editForm.name, email: editForm.email, role: editForm.role }); setEditingId(null); await fetchUsers(); } catch (err) { setError(err.message || 'Не удалось сохранить.'); } finally { setSaving(false); } }; - /* ── Delete ── */ + /* ── Delete via edge function ── */ const handleDeleteUser = async (userId) => { try { - const { error: err } = await supabase.from('users').delete().eq('id', userId); - if (err) throw err; - try { await supabase.auth.admin.deleteUser(userId); } catch {} + await adminApi('DELETE', { id: userId }); setDeleteConfirmId(null); await fetchUsers(); } catch (err) { setError(err.message || 'Не удалось удалить.'); } @@ -197,38 +206,34 @@ export default function UserManagementPanel() {
- {/* Add form */} + {/* Add form — compact column */} {showAddForm && ( -
-
- setAddForm((f) => ({ ...f, name: e.target.value }))} - className="flex-1 min-w-[140px]!" - /> - setAddForm((f) => ({ ...f, email: e.target.value }))} - className="flex-1 min-w-[180px]!" - /> - setAddForm((f) => ({ ...f, role: r }))} - /> -
+ + setAddForm((f) => ({ ...f, name: e.target.value }))} + className="w-[220px]!" + /> + setAddForm((f) => ({ ...f, email: e.target.value }))} + className="w-[260px]!" + /> + setAddForm((f) => ({ ...f, role: r }))} + /> {addError &&
{addError}
} -
- -
+ )} @@ -252,29 +257,28 @@ export default function UserManagementPanel() { return (
-
+
setEditForm((f) => ({ ...f, name: e.target.value }))} - className="flex-1 min-w-[140px]!" + className="w-[200px]!" /> setEditForm((f) => ({ ...f, email: e.target.value }))} - className="flex-1 min-w-[180px]!" + className="w-[240px]!" /> setEditForm((f) => ({ ...f, role: r }))} - onClose={undefined} />
-
+
+ {deleteConfirmId === user.id ? ( +
+ + +
+ ) : ( - {deleteConfirmId === user.id ? ( - <> - - - - ) : ( - - )} -
+ onClick={() => setDeleteConfirmId(user.id)} + className="rounded-lg border border-[var(--color-danger)] px-2.5 py-1 text-xs font-semibold text-[var(--color-danger)] hover:bg-[rgba(201,61,61,0.08)] transition" + >Удалить + )}
);