99 lines
4.9 KiB
JavaScript
99 lines
4.9 KiB
JavaScript
// 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 }));
|
||
}
|