// Modal with the form for creating and editing a user import { alert, api, h } from './common.js'; import { openModal } from './modal.js'; const ROLES = { trainee: 'Тренирующийся', trainer: 'Тренер' }; const jsonRequest = (method, body) => ({ method, headers: { 'Content-Type': 'application/json' }, body: body === undefined ? undefined : JSON.stringify(body), }); function field(label, input, className = 'col-12 col-md-6') { return h('div', { class: className }, h('label', { class: 'form-label small text-muted mb-1' }, label), input); } function errorText(error) { if (error.status === 409) return 'Пользователь с таким email уже есть.'; if (error.status === 422) return 'Проверьте поля: email должен быть корректным, пароль — не короче 8 символов.'; if (error.status === 404) return 'Пользователь не найден.'; return error.message; } /** user is null for a new user; onChange() is called after it was saved or deleted */ function userForm(user, { onDone, onCancel }) { const isNew = !user; const input = (name, attrs) => h('input', { class: 'form-control', name, ...attrs }); const status = h('div', { class: 'mt-3' }); const save = h('button', { type: 'submit', class: 'btn btn-primary' }, isNew ? 'Создать' : 'Сохранить'); const cancel = h('button', { type: 'button', class: 'btn btn-outline-secondary' }, 'Отмена'); cancel.addEventListener('click', onCancel); const remove = isNew ? null : h('button', { type: 'button', class: 'btn btn-outline-danger ms-auto' }, 'Удалить'); const role = h('select', { class: 'form-select', name: 'role' }, ...Object.entries(ROLES).map(([value, title]) => h('option', { value, selected: (user?.role || 'trainee') === value }, title))); const form = h('form', {}, h('div', { class: 'row g-3' }, field('Email', input('email', { type: 'email', required: true, maxlength: 254, value: user?.email || '', autocomplete: 'off' })), field(isNew ? 'Пароль' : 'Новый пароль', input('password', { type: 'password', minlength: 8, maxlength: 128, required: isNew, autocomplete: 'new-password', placeholder: isNew ? 'не короче 8 символов' : 'оставьте пустым, чтобы не менять', })), field('Имя', input('first_name', { type: 'text', maxlength: 100, value: user?.first_name || '' })), field('Фамилия', input('last_name', { type: 'text', maxlength: 100, value: user?.last_name || '' })), field('Роль', role), isNew ? null : h('div', { class: 'col-12 col-md-6 d-flex align-items-end' }, h('div', { class: 'form-check mb-2' }, h('input', { type: 'checkbox', class: 'form-check-input', name: 'is_active', id: 'user-is-active', checked: user.is_active }), h('label', { class: 'form-check-label', for: 'user-is-active' }, 'Активен')))), h('div', { class: 'd-flex gap-2 align-items-center mt-4' }, save, cancel, remove), status); form.addEventListener('submit', async (event) => { event.preventDefault(); const { elements } = form; const data = { email: elements.email.value, first_name: elements.first_name.value, last_name: elements.last_name.value, role: elements.role.value, }; if (elements.password.value) data.password = elements.password.value; save.disabled = true; try { if (isNew) { await api('/users/', jsonRequest('POST', data)); } else { data.is_active = elements.is_active.checked; await api(`/users/${user.id}/`, jsonRequest('PATCH', data)); } onDone(); } catch (error) { status.replaceChildren(alert('danger', `Не удалось сохранить: ${errorText(error)}`)); save.disabled = false; } }); remove?.addEventListener('click', async () => { if (!confirm(`Удалить пользователя ${user.email}?`)) return; try { await api(`/users/${user.id}/`, jsonRequest('DELETE')); onDone(); } catch (error) { status.replaceChildren(alert('danger', `Не удалось удалить: ${errorText(error)}`)); } }); return form; } /** Opens the form; user is the existing user to edit or null to create one. */ export function openUserDialog(user, onChange) { const { body, hide } = openModal(user ? 'Редактирование пользователя' : 'Новый пользователь'); body.replaceChildren(userForm(user, { onDone: () => { hide(); onChange(); }, onCancel: hide })); }