|
|
@@ -24,12 +24,68 @@ const statusLabels: Record<string, string> = {
|
|
|
* пользователей по роли, изменять роль, блокировать/разблокировать.
|
|
|
* Загружает до 100 пользователей через API /admin/users.
|
|
|
*/
|
|
|
+function CreateUserModal({ onClose, onCreated }: { onClose: () => void; onCreated: () => void }) {
|
|
|
+ const [email, setEmail] = useState('')
|
|
|
+ const [password, setPassword] = useState('')
|
|
|
+ const [role, setRole] = useState('customer')
|
|
|
+ const [name, setName] = useState('')
|
|
|
+ const [error, setError] = useState('')
|
|
|
+ const [saving, setSaving] = useState(false)
|
|
|
+
|
|
|
+ const handleSubmit = async (e: React.FormEvent) => {
|
|
|
+ e.preventDefault()
|
|
|
+ setSaving(true)
|
|
|
+ setError('')
|
|
|
+ try {
|
|
|
+ await api.post('/admin/users', { email, password, role, name: name || undefined })
|
|
|
+ onCreated()
|
|
|
+ } catch (err: any) {
|
|
|
+ setError(err.message || 'Ошибка')
|
|
|
+ } finally {
|
|
|
+ setSaving(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={onClose}>
|
|
|
+ <div className="w-full max-w-sm rounded-xl bg-[#1e293b] p-6 shadow-xl" onClick={(e) => e.stopPropagation()}>
|
|
|
+ <h2 className="mb-4 text-lg font-bold text-white">Создать пользователя</h2>
|
|
|
+ {error && <p className="mb-3 text-sm text-red-400">{error}</p>}
|
|
|
+ <form onSubmit={handleSubmit} className="space-y-3">
|
|
|
+ <input type="text" placeholder="Имя" value={name} onChange={(e) => setName(e.target.value)}
|
|
|
+ className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-sm text-white placeholder-white/40 outline-none focus:border-green-500" />
|
|
|
+ <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} required
|
|
|
+ className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-sm text-white placeholder-white/40 outline-none focus:border-green-500" />
|
|
|
+ <input type="password" placeholder="Пароль" value={password} onChange={(e) => setPassword(e.target.value)} required minLength={8}
|
|
|
+ className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-sm text-white placeholder-white/40 outline-none focus:border-green-500" />
|
|
|
+ <select value={role} onChange={(e) => setRole(e.target.value)}
|
|
|
+ className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-sm text-white outline-none focus:border-green-500"
|
|
|
+ >
|
|
|
+ {Object.entries(roleLabels).map(([k, v]) => (
|
|
|
+ <option key={k} value={k} className="bg-[#1e293b]">{v}</option>
|
|
|
+ ))}
|
|
|
+ </select>
|
|
|
+ <div className="flex items-center justify-end gap-3 pt-2">
|
|
|
+ <button type="button" onClick={onClose}
|
|
|
+ className="rounded-lg px-4 py-2 text-sm text-white/60 hover:text-white transition"
|
|
|
+ >Отмена</button>
|
|
|
+ <button type="submit" disabled={saving}
|
|
|
+ className="rounded-lg bg-green-600 px-4 py-2 text-sm text-white hover:bg-green-500 transition disabled:opacity-50"
|
|
|
+ >{saving ? 'Создание...' : 'Создать'}</button>
|
|
|
+ </div>
|
|
|
+ </form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
export default function AdminUsersPage() {
|
|
|
const { user: currentUser } = useAuth()
|
|
|
const [users, setUsers] = useState<User[]>([])
|
|
|
const [loading, setLoading] = useState(true)
|
|
|
const [roleFilter, setRoleFilter] = useState('')
|
|
|
const [msg, setMsg] = useState('')
|
|
|
+ const [showCreate, setShowCreate] = useState(false)
|
|
|
|
|
|
const fetchUsers = useCallback(async () => {
|
|
|
const params = new URLSearchParams()
|
|
|
@@ -61,7 +117,12 @@ export default function AdminUsersPage() {
|
|
|
|
|
|
return (
|
|
|
<div>
|
|
|
- <h1 className="mb-6 text-2xl font-bold text-white">Управление пользователями</h1>
|
|
|
+ <div className="mb-6 flex items-center justify-between">
|
|
|
+ <h1 className="text-2xl font-bold text-white">Управление пользователями</h1>
|
|
|
+ <button onClick={() => setShowCreate(true)}
|
|
|
+ className="rounded-lg bg-green-600 px-4 py-2 text-sm text-white hover:bg-green-500 transition"
|
|
|
+ >+ Создать</button>
|
|
|
+ </div>
|
|
|
{msg && <p className="mb-4 rounded-lg bg-blue-500/10 px-4 py-2 text-sm text-blue-400">{msg}</p>}
|
|
|
|
|
|
<div className="mb-4 flex flex-wrap gap-2">
|
|
|
@@ -126,6 +187,10 @@ export default function AdminUsersPage() {
|
|
|
</div>
|
|
|
)
|
|
|
})()}
|
|
|
+
|
|
|
+ {showCreate && (
|
|
|
+ <CreateUserModal onClose={() => setShowCreate(false)} onCreated={() => { setShowCreate(false); setMsg('Пользователь создан'); fetchUsers() }} />
|
|
|
+ )}
|
|
|
</div>
|
|
|
)
|
|
|
}
|