From 4b244bf0bb51fbb449fdaa91654fd5242411f7bd Mon Sep 17 00:00:00 2001 From: houseassassin Date: Sun, 10 May 2026 13:09:30 +0300 Subject: [PATCH] feat: add UserManagementPage component --- src/admin/users/UserManagementPage.jsx | 274 +++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 src/admin/users/UserManagementPage.jsx diff --git a/src/admin/users/UserManagementPage.jsx b/src/admin/users/UserManagementPage.jsx new file mode 100644 index 0000000..99c496d --- /dev/null +++ b/src/admin/users/UserManagementPage.jsx @@ -0,0 +1,274 @@ +import { useState } from 'react'; +import { Users, Trash2 } from 'lucide-react'; +import { useAdminUsers } from './useAdminUsers'; + +const inputStyle = { + padding: '10px 12px', + background: 'var(--bg-lighter)', + border: '1.5px solid var(--border-light)', + borderRadius: '6px', + color: 'var(--text-main)', + fontSize: '0.92rem', + outline: 'none', + fontFamily: 'var(--font-body)', + boxSizing: 'border-box', + flex: 1, +}; + +const labelStyle = { + display: 'block', + marginBottom: '5px', + fontSize: '0.75rem', + fontWeight: 700, + color: 'var(--text-muted)', + textTransform: 'uppercase', + letterSpacing: '0.4px', +}; + +const thStyle = { + padding: '10px 14px', + textAlign: 'left', + fontSize: '0.72rem', + fontWeight: 700, + color: 'var(--text-muted)', + textTransform: 'uppercase', + letterSpacing: '0.4px', + borderBottom: '1.5px solid var(--border-light)', + fontFamily: 'var(--font-heading)', +}; + +const tdStyle = { + padding: '12px 14px', + fontSize: '0.88rem', + color: 'var(--text-main)', + borderBottom: '1px solid var(--border-light)', + fontFamily: 'var(--font-body)', +}; + +const formatDate = (dateStr) => { + if (!dateStr) return '—'; + return new Date(dateStr).toLocaleDateString('ru-RU'); +}; + +const UserManagementPage = () => { + const { users, loading, error, inviteUser, deleteUser } = useAdminUsers(); + + const [inviteEmail, setInviteEmail] = useState(''); + const [inviting, setInviting] = useState(false); + const [inviteError, setInviteError] = useState(''); + + const [confirmDeleteId, setConfirmDeleteId] = useState(null); + const [deleting, setDeleting] = useState(false); + + const handleInvite = async (e) => { + e.preventDefault(); + setInviteError(''); + setInviting(true); + try { + await inviteUser(inviteEmail); + setInviteEmail(''); + } catch (err) { + setInviteError(err.message ?? 'Ошибка приглашения'); + } finally { + setInviting(false); + } + }; + + const handleDeleteConfirm = async (userId) => { + setDeleting(true); + try { + await deleteUser(userId); + } catch { + // deletion errors surface in the hook's error state + } finally { + setDeleting(false); + setConfirmDeleteId(null); + } + }; + + return ( +
+

+ + Пользователи +

+ + {/* Invite form */} +
+

+ Пригласить пользователя +

+ +
+ +
+ setInviteEmail(e.target.value)} + required + style={inputStyle} + /> + +
+ {inviteError && ( +

+ {inviteError} +

+ )} +
+
+ + {/* Users table */} +
+

+ Администраторы +

+ + {error && ( +

+ {error} +

+ )} + + {loading ? ( +
Загрузка...
+ ) : ( +
+ + + + + + + + + + + {users.map((user) => ( + + + + + + + ))} + {users.length === 0 && ( + + + + )} + +
EmailДата созданияПоследний вход
{user.email}{formatDate(user.created_at)}{formatDate(user.last_sign_in_at)} + {confirmDeleteId === user.id ? ( + + + Вы уверены? + + + + + ) : ( + + )} +
+ Нет пользователей +
+
+ )} +
+
+ ); +}; + +export default UserManagementPage;