HM-125. Добавление работы с пользователями (#59)
This commit is contained in:
@ -31,7 +31,7 @@ class UsersService {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMe = async () => {
|
getSelfInfo = async () => {
|
||||||
const {data} = await http.get(`${ROOT_URL}/me`);
|
const {data} = await http.get(`${ROOT_URL}/me`);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -69,8 +69,8 @@ class UsersService {
|
|||||||
* @param {string} login - Логин пользователя
|
* @param {string} login - Логин пользователя
|
||||||
* @param {UpdateUserOptions} updateOptions - настройки для обновления пользователя
|
* @param {UpdateUserOptions} updateOptions - настройки для обновления пользователя
|
||||||
*/
|
*/
|
||||||
update = async (login, updateOptions) => {
|
update = async (user) => {
|
||||||
const {data} = await http.put(ROOT_URL, {...updateOptions, login});
|
const {data} = await http.put(ROOT_URL, user);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ html, body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Page > div:not(.Login__page) {
|
.Page > div:first-child:not(.Login__page) {
|
||||||
padding-top: 74px;
|
padding-top: 74px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
src/app.html
15
src/app.html
@ -390,6 +390,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- Шаблоны форм для просмотры логов -->
|
||||||
|
<template id="user-view-form">
|
||||||
|
<div class="h-100 overflow-auto d-flex flex-column">
|
||||||
|
<p class="h2 mb-2 p-3 pr-5 sticky-top border-bottom bg-light"></p>
|
||||||
|
<form class="p-3 h" autocomplete="off"></form>
|
||||||
|
<div class="mt-auto p-3 pr-5 border-top bg-light">
|
||||||
|
<button type="button" class="btn btn-primary UserViewForm__create">Создать</button>
|
||||||
|
<button type="button" class="btn btn-primary UserViewForm__save">Сохранить</button>
|
||||||
|
<button type="button" class="btn btn-warning UserViewForm__edit">Изменить</button>
|
||||||
|
<button type="button" class="btn btn-danger UserViewForm__delete">Удалить</button>
|
||||||
|
<button type="button" class="btn btn-secondary UserViewForm__cancel">Отмена</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- Шаблоны форм для просмотры api -->
|
<!-- Шаблоны форм для просмотры api -->
|
||||||
<template id="api-view-form">
|
<template id="api-view-form">
|
||||||
<div class="h-100 overflow-auto Api__view-container">
|
<div class="h-100 overflow-auto Api__view-container">
|
||||||
|
|||||||
@ -53,7 +53,7 @@ class AvatarModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init = async () => {
|
init = async () => {
|
||||||
const user = await usersServiceApi.getMe();
|
const user = await usersServiceApi.getSelfInfo();
|
||||||
this.changeAvatar(this.url || user.avatar);
|
this.changeAvatar(this.url || user.avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import FormControl from '../form-control';
|
|||||||
import {FORM_TYPES} from '../../consts';
|
import {FORM_TYPES} from '../../consts';
|
||||||
import ModalSidebar from '../modal-sidebar';
|
import ModalSidebar from '../modal-sidebar';
|
||||||
import './ClientLogsViewForm.css';
|
import './ClientLogsViewForm.css';
|
||||||
|
import {INPUT_IDS, LABELS, CLASSNAMES} from './consts';
|
||||||
|
|
||||||
class ClientLogsViewForm extends Component {
|
class ClientLogsViewForm extends Component {
|
||||||
constructor (parentNode) {
|
constructor (parentNode) {
|
||||||
@ -15,44 +16,50 @@ class ClientLogsViewForm extends Component {
|
|||||||
this.title = this.mainNode.querySelector('.h2');
|
this.title = this.mainNode.querySelector('.h2');
|
||||||
this.form = this.mainNode.querySelector('form');
|
this.form = this.mainNode.querySelector('form');
|
||||||
|
|
||||||
this.title.textContent = 'Просмотр клиентского запроса';
|
this.title.textContent = LABELS.VIEW_CLIENT_REQUEST;
|
||||||
|
|
||||||
const inputs = [
|
const inputs = [
|
||||||
this.idInput = this.createComponent(FormControl, this.form, {
|
this.idInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-id',
|
id: INPUT_IDS.ID,
|
||||||
label: 'id',
|
label: LABELS.ID,
|
||||||
}),
|
}),
|
||||||
this.resultInput = this.createComponent(FormControl, this.form, {
|
this.resultInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-result',
|
id: INPUT_IDS.RESULT,
|
||||||
label: 'Результат',
|
label: LABELS.RESULT,
|
||||||
}),
|
}),
|
||||||
this.startTimeInput = this.createComponent(FormControl, this.form, {
|
this.startTimeInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-startTime',
|
id: INPUT_IDS.START_TIME,
|
||||||
label: 'Время запроса',
|
label: LABELS.TIME_REQUEST,
|
||||||
}),
|
}),
|
||||||
this.headersInput = this.createComponent(FormControl, this.form, {
|
this.headersInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-headers',
|
id: INPUT_IDS.HEADERS,
|
||||||
label: 'Заголовки запроса',
|
label: LABELS.HEADERS_REQUEST,
|
||||||
type: FORM_TYPES.TEXTAREA,
|
type: FORM_TYPES.TEXTAREA,
|
||||||
className: 'ClientLogsViewForm__headersInput',
|
className: CLASSNAMES.HEADERS_INPUT,
|
||||||
|
}),
|
||||||
|
this.bodyRequest = this.createComponent(FormControl, this.form, {
|
||||||
|
id: INPUT_IDS.BODY,
|
||||||
|
label: LABELS.BODY_REQUEST,
|
||||||
|
type: FORM_TYPES.TEXTAREA,
|
||||||
|
className: CLASSNAMES.HEADERS_INPUT,
|
||||||
}),
|
}),
|
||||||
this.urlInput = this.createComponent(FormControl, this.form, {
|
this.urlInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-url',
|
id: INPUT_IDS.URL,
|
||||||
label: 'URL запроса',
|
label: LABELS.URL_REQUEST,
|
||||||
}),
|
}),
|
||||||
this.methodInput = this.createComponent(FormControl, this.form, {
|
this.methodInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-method',
|
id: INPUT_IDS.METHOD,
|
||||||
label: 'Метод запроса',
|
label: LABELS.METHOD_REQUEST,
|
||||||
}),
|
}),
|
||||||
this.endTimeInput = this.createComponent(FormControl, this.form, {
|
this.endTimeInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-endTime',
|
id: INPUT_IDS.END_TIME,
|
||||||
label: 'Время ответа',
|
label: LABELS.TIME_RESPONSE,
|
||||||
}),
|
}),
|
||||||
this.responseInput = this.createComponent(FormControl, this.form, {
|
this.responseInput = this.createComponent(FormControl, this.form, {
|
||||||
id: 'client-logs-view-form-response',
|
id: INPUT_IDS.RESPONSE,
|
||||||
label: 'Ответ сервера',
|
label: LABELS.SERVER_RESPONSE,
|
||||||
type: FORM_TYPES.TEXTAREA,
|
type: FORM_TYPES.TEXTAREA,
|
||||||
className: 'ClientLogsViewForm__responseInput',
|
className: CLASSNAMES.RESPONSE_INPUT,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
inputs.forEach((input) => {
|
inputs.forEach((input) => {
|
||||||
@ -65,8 +72,13 @@ class ClientLogsViewForm extends Component {
|
|||||||
return JSON.stringify(object, false, 4);
|
return JSON.stringify(object, false, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
setForm ({_id, type, request, response, startTime, endTime}) {
|
setRequestBody = (body) => {
|
||||||
const {headers, url, method} = JSON.parse(request) ?? {};
|
this.bodyRequest.setValue(this.prepareStringJSON(body));
|
||||||
|
this.bodyRequest.mainNode.style.display = body ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
setForm = ({_id, type, request, response, startTime, endTime}) => {
|
||||||
|
const {headers, url, method, body} = JSON.parse(request) ?? {};
|
||||||
this.idInput.setValue(_id);
|
this.idInput.setValue(_id);
|
||||||
this.resultInput.setValue(type);
|
this.resultInput.setValue(type);
|
||||||
this.startTimeInput.setValue(startTime);
|
this.startTimeInput.setValue(startTime);
|
||||||
@ -75,6 +87,7 @@ class ClientLogsViewForm extends Component {
|
|||||||
this.methodInput.setValue(method);
|
this.methodInput.setValue(method);
|
||||||
this.endTimeInput.setValue(endTime);
|
this.endTimeInput.setValue(endTime);
|
||||||
this.responseInput.setValue(this.prepareStringJSON(response));
|
this.responseInput.setValue(this.prepareStringJSON(response));
|
||||||
|
this.setRequestBody(body);
|
||||||
this.sidebar.show();
|
this.sidebar.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/components/client-logs-view-form/consts.js
Normal file
29
src/components/client-logs-view-form/consts.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export const INPUT_IDS = {
|
||||||
|
ID: 'client-logs-view-form-id',
|
||||||
|
RESULT: 'client-logs-view-form-result',
|
||||||
|
START_TIME: 'client-logs-view-form-startTime',
|
||||||
|
HEADERS: 'client-logs-view-form-headers',
|
||||||
|
BODY: 'client-logs-view-form-body-request',
|
||||||
|
URL: 'client-logs-view-form-url',
|
||||||
|
METHOD: 'client-logs-view-form-method',
|
||||||
|
END_TIME: 'client-logs-view-form-endTime',
|
||||||
|
RESPONSE: 'client-logs-view-form-response',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LABELS = {
|
||||||
|
ID: 'id',
|
||||||
|
RESULT: 'Результат',
|
||||||
|
TIME_REQUEST: 'Время запроса',
|
||||||
|
HEADERS_REQUEST: 'Заголовки запроса',
|
||||||
|
BODY_REQUEST: 'Тело запроса',
|
||||||
|
URL_REQUEST: 'URL запроса',
|
||||||
|
METHOD_REQUEST: 'Метод запроса',
|
||||||
|
TIME_RESPONSE: 'Время ответа',
|
||||||
|
SERVER_RESPONSE: 'Ответ сервера',
|
||||||
|
VIEW_CLIENT_REQUEST: 'Просмотр клиентского запроса',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CLASSNAMES = {
|
||||||
|
HEADERS_INPUT: 'ClientLogsViewForm__headersInput',
|
||||||
|
RESPONSE_INPUT: 'ClientLogsViewForm__responseInput',
|
||||||
|
};
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import Component from '../component';
|
import Component from '../component';
|
||||||
import {FORM_TYPES} from '../../consts';
|
import {FORM_TYPES, EVENTS, TAG_NAME} from '../../consts';
|
||||||
|
|
||||||
class FormControl extends Component {
|
class FormControl extends Component {
|
||||||
constructor (parentNode, {
|
constructor (parentNode, {
|
||||||
@ -7,9 +7,11 @@ class FormControl extends Component {
|
|||||||
id,
|
id,
|
||||||
type = FORM_TYPES.TEXT,
|
type = FORM_TYPES.TEXT,
|
||||||
placeholder = '',
|
placeholder = '',
|
||||||
|
pattern,
|
||||||
initValue = '',
|
initValue = '',
|
||||||
className = '',
|
className = '',
|
||||||
required = false,
|
required = false,
|
||||||
|
options = [],
|
||||||
} = {}) {
|
} = {}) {
|
||||||
super('#form-control', parentNode);
|
super('#form-control', parentNode);
|
||||||
|
|
||||||
@ -18,11 +20,12 @@ class FormControl extends Component {
|
|||||||
this.input = this.createElement({
|
this.input = this.createElement({
|
||||||
tagName: this.getInputTagName(type),
|
tagName: this.getInputTagName(type),
|
||||||
options: {
|
options: {
|
||||||
className: `form-control ${className}`,
|
className: `${this.getInputBaseClassName(type)} ${className}`,
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
type: type === FORM_TYPES.PASSWORD ? 'password' : 'text',
|
type: type === FORM_TYPES.PASSWORD ? 'password' : 'text',
|
||||||
...(required ? {required: ''} : {}),
|
...(required ? {required: ''} : {}),
|
||||||
|
...(pattern ? {pattern} : {}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.input.placeholder = placeholder;
|
this.input.placeholder = placeholder;
|
||||||
@ -33,12 +36,31 @@ class FormControl extends Component {
|
|||||||
this.label.textContent = label;
|
this.label.textContent = label;
|
||||||
this.label.setAttribute('for', id);
|
this.label.setAttribute('for', id);
|
||||||
|
|
||||||
this.addEventListener(this.input, 'focus', this.clearError);
|
this.addEventListener(this.input, EVENTS.FOCUS, this.clearError);
|
||||||
this.addEventListener(this.input, 'click', this.clearError);
|
this.addEventListener(this.input, EVENTS.CLICK, this.clearError);
|
||||||
this.addEventListener(this.input, 'keydown', this.clearError);
|
this.addEventListener(this.input, EVENTS.KEYDOWN, this.clearError);
|
||||||
this.addEventListener(this.input, 'input', (evt) => {
|
this.addEventListener(this.input, EVENTS.INPUT, (evt) => {
|
||||||
this.next('input', evt);
|
this.next(EVENTS.INPUT, evt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (type === FORM_TYPES.SELECT) {
|
||||||
|
options.forEach(({value, text}) => {
|
||||||
|
this.createElement({
|
||||||
|
tagName: TAG_NAME.OPTION,
|
||||||
|
parentNode: this.input,
|
||||||
|
options: {
|
||||||
|
textContent: text,
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addEventListener(this.input, EVENTS.CHANGE, (evt) => {
|
||||||
|
this.next(EVENTS.CHANGE, evt);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled = (value) => {
|
disabled = (value) => {
|
||||||
@ -61,20 +83,29 @@ class FormControl extends Component {
|
|||||||
this.errorText.textContent = '';
|
this.errorText.textContent = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
getInputTagName (type) {
|
getInputTagName = (type) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case FORM_TYPES.TEXT:
|
case FORM_TYPES.TEXT:
|
||||||
case FORM_TYPES.PASSWORD:
|
case FORM_TYPES.PASSWORD:
|
||||||
return 'input';
|
return TAG_NAME.INPUT;
|
||||||
case FORM_TYPES.SELECT:
|
case FORM_TYPES.SELECT:
|
||||||
return 'select';
|
return TAG_NAME.SELECT;
|
||||||
case FORM_TYPES.TEXTAREA:
|
case FORM_TYPES.TEXTAREA:
|
||||||
return 'textarea';
|
return TAG_NAME.TEXTAREA;
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`Тип формы ${type} не поддерживается`);
|
throw new Error(`Тип формы ${type} не поддерживается`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getInputBaseClassName = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case FORM_TYPES.SELECT:
|
||||||
|
return 'form-select';
|
||||||
|
default:
|
||||||
|
return 'form-control';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FormControl;
|
export default FormControl;
|
||||||
|
|||||||
@ -23,7 +23,7 @@ class ProfilePage extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init = async () => {
|
init = async () => {
|
||||||
this.user = await usersServiceApi.getMe();
|
this.user = await usersServiceApi.getSelfInfo();
|
||||||
this.form.initProfile(this.user);
|
this.form.initProfile(this.user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
236
src/components/user-view-form/UserViewForm.js
Normal file
236
src/components/user-view-form/UserViewForm.js
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
import Component from '../component';
|
||||||
|
import ModalSidebar from '../modal-sidebar';
|
||||||
|
import FormControl from '../form-control';
|
||||||
|
import {FORM_TYPES, EVENTS, MODES} from '../../consts';
|
||||||
|
import routeService from '../../services/RouteService';
|
||||||
|
import usersServiceApi from '../../api/UsersServiceAPI';
|
||||||
|
import userInfoService from '../../services/UserInfoService';
|
||||||
|
|
||||||
|
const TITLE_MODES = {
|
||||||
|
[MODES.Create]: 'Создание пользователя',
|
||||||
|
[MODES.View]: 'Просмотр пользователя',
|
||||||
|
[MODES.Edit]: 'Редактирование пользователя',
|
||||||
|
};
|
||||||
|
|
||||||
|
const EDIT_MODES = [MODES.Create, MODES.Edit];
|
||||||
|
|
||||||
|
const TRUE = 'true';
|
||||||
|
|
||||||
|
class UserViewForm extends Component {
|
||||||
|
constructor (parentNode) {
|
||||||
|
super('#user-view-form', parentNode);
|
||||||
|
|
||||||
|
this.sidebar = this.createComponent(ModalSidebar, {
|
||||||
|
content: this.mainNode,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.title = this.mainNode.querySelector('.h2');
|
||||||
|
this.form = this.mainNode.querySelector('form');
|
||||||
|
|
||||||
|
this.inputs = [
|
||||||
|
this.login = this.createComponent(FormControl, this.form, {
|
||||||
|
id: 'user-view-form-login',
|
||||||
|
label: 'Логин пользователя',
|
||||||
|
pattern: '^[a-zA-Z][a-zA-Z0-9_-]{3,}$',
|
||||||
|
required: true,
|
||||||
|
offComplete: true,
|
||||||
|
}),
|
||||||
|
this.password = this.createComponent(FormControl, this.form, {
|
||||||
|
id: 'user-view-form-password',
|
||||||
|
label: 'Пароль',
|
||||||
|
type: FORM_TYPES.PASSWORD,
|
||||||
|
}),
|
||||||
|
this.avatar = this.createComponent(FormControl, this.form, {
|
||||||
|
id: 'user-view-form-avatar',
|
||||||
|
label: 'Аватар',
|
||||||
|
}),
|
||||||
|
this.isAdmin = this.createComponent(FormControl, this.form, {
|
||||||
|
id: 'user-view-form-is-admin',
|
||||||
|
label: 'Админ',
|
||||||
|
type: FORM_TYPES.SELECT,
|
||||||
|
options: [
|
||||||
|
{value: true, text: 'Да'},
|
||||||
|
{value: false, text: 'Нет'},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
this.password.input.setAttribute('autocomplete', 'new-password');
|
||||||
|
|
||||||
|
this.buttons = [
|
||||||
|
{
|
||||||
|
button: this.createButton = this.mainNode.querySelector('.UserViewForm__create'),
|
||||||
|
modes: [MODES.Create],
|
||||||
|
onlyAdmin: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
button: this.saveButton = this.mainNode.querySelector('.UserViewForm__save'),
|
||||||
|
modes: [MODES.Edit],
|
||||||
|
onlyAdmin: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
button: this.editButton = this.mainNode.querySelector('.UserViewForm__edit'),
|
||||||
|
modes: [MODES.View],
|
||||||
|
onlyAdmin: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
button: this.cancelButton = this.mainNode.querySelector('.UserViewForm__cancel'),
|
||||||
|
modes: [MODES.Create, MODES.Edit, MODES.View],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
button: this.deleteButton = this.mainNode.querySelector('.UserViewForm__delete'),
|
||||||
|
modes: [MODES.View, MODES.Edit],
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
this.addEventListener(this.cancelButton, 'click', () => {
|
||||||
|
routeService.pushQuery({}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addEventListener(this.editButton, 'click', () => {
|
||||||
|
routeService.pushQuery({mode: MODES.Edit});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addEventListener(this.form, 'submit', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
this.addEventListener(this.createButton, 'click', this.createUser);
|
||||||
|
this.addEventListener(this.saveButton, 'click', this.saveUser);
|
||||||
|
this.addEventListener(this.deleteButton, 'click', this.deleteUser);
|
||||||
|
|
||||||
|
this.addSubscribe(routeService, EVENTS.ROUTE_SEARCH_CHANGE, ({query}) => {
|
||||||
|
const {mode, login} = query;
|
||||||
|
this.setForm(login, mode);
|
||||||
|
});
|
||||||
|
|
||||||
|
const {query: {mode, login}} = routeService.getUrlData();
|
||||||
|
if (mode) {
|
||||||
|
this.setForm(login, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addSubscribe(userInfoService, EVENTS.CHANGE_USER_INFO, ({is_admin}) => {
|
||||||
|
this.createButton.disabled = !is_admin;
|
||||||
|
this.createButton.saveButton = !is_admin;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setSidebarTitle = (mode, userName) => {
|
||||||
|
this.title.textContent = [TITLE_MODES[mode], userName].filter(Boolean).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
setInputDisabled = (mode) => {
|
||||||
|
const disabled = !EDIT_MODES.includes(mode);
|
||||||
|
this.inputs.forEach((input) => {
|
||||||
|
const disabledLogin = this.login === input && mode === MODES.Edit;
|
||||||
|
input.disabled(disabled ? disabled : disabledLogin);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
validateInputs = () => {
|
||||||
|
this.form.classList.add('was-validated');
|
||||||
|
const login = this.login.getValue();
|
||||||
|
|
||||||
|
const loginErrorMessage = (() => {
|
||||||
|
if (!login) {
|
||||||
|
return 'Заполните логин';
|
||||||
|
}
|
||||||
|
if (login.length < 4) {
|
||||||
|
return 'Длинна логина минимум 4 символа';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[a-z][a-z0-9_-]*$/.test(login)) {
|
||||||
|
return 'Логин может содержать только латинские буквы, цифры, тире и нижнее подчеркивание';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
})();
|
||||||
|
|
||||||
|
this.login.setError(loginErrorMessage);
|
||||||
|
|
||||||
|
return this.form.checkValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
createUser = () => {
|
||||||
|
if (this.validateInputs()) {
|
||||||
|
this.next(EVENTS.CREATE_USER, {
|
||||||
|
login: this.login.getValue(),
|
||||||
|
avatar: this.avatar.getValue(),
|
||||||
|
password: this.password.getValue(),
|
||||||
|
is_admin: this.isAdmin.getValue() === TRUE,
|
||||||
|
});
|
||||||
|
routeService.pushQuery({}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveUser = () => {
|
||||||
|
if (this.validateInputs()) {
|
||||||
|
this.next(EVENTS.SAVE_USER, {
|
||||||
|
login: this.login.getValue(),
|
||||||
|
avatar: this.avatar.getValue(),
|
||||||
|
is_admin: this.isAdmin.getValue() === TRUE,
|
||||||
|
});
|
||||||
|
routeService.pushQuery({}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteUser = () => {
|
||||||
|
const {query: {login}} = routeService.getUrlData();
|
||||||
|
routeService.pushQuery({}, true);
|
||||||
|
this.next(EVENTS.DELETE_USER, login);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUser = async (login) => {
|
||||||
|
if (login) {
|
||||||
|
return await usersServiceApi.find(login);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
login: '',
|
||||||
|
avatar: '',
|
||||||
|
is_admin: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
showButtons = async (mode) => {
|
||||||
|
const user = await userInfoService.getUserInfo();
|
||||||
|
this.buttons.forEach(({button, modes, onlyAdmin}) => {
|
||||||
|
const isShow = modes.includes(mode) && (onlyAdmin ? user.is_admin : true);
|
||||||
|
button.style.display = isShow ? 'inline-block' : 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setPassword = (mode) => {
|
||||||
|
const isShow = mode === MODES.Create;
|
||||||
|
|
||||||
|
this.password.setValue('');
|
||||||
|
|
||||||
|
this.password.mainNode.style.display = isShow ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
setLogin = (mode, login) => {
|
||||||
|
const disabled = mode !== MODES.Create;
|
||||||
|
|
||||||
|
this.login.disabled(disabled);
|
||||||
|
|
||||||
|
this.login.setValue(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
setForm = async (userLogin, mode) => {
|
||||||
|
const {login, avatar, is_admin} = await this.loadUser(userLogin);
|
||||||
|
this.setSidebarTitle(mode, login);
|
||||||
|
this.setLogin(mode, login);
|
||||||
|
this.avatar.setValue(avatar);
|
||||||
|
this.isAdmin.setValue(!!is_admin);
|
||||||
|
this.setInputDisabled(mode);
|
||||||
|
this.showButtons(mode);
|
||||||
|
this.setPassword(mode);
|
||||||
|
|
||||||
|
if (mode) {
|
||||||
|
this.sidebar.show();
|
||||||
|
} else {
|
||||||
|
this.sidebar.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserViewForm;
|
||||||
3
src/components/user-view-form/index.js
Normal file
3
src/components/user-view-form/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import UserViewForm from './UserViewForm';
|
||||||
|
|
||||||
|
export default UserViewForm;
|
||||||
@ -1,16 +1,69 @@
|
|||||||
import Component from '../component';
|
import Component from '../component';
|
||||||
import UsersTable from '../users-table';
|
import UsersTable from '../users-table';
|
||||||
import usersServiceApi from '../../api/UsersServiceAPI';
|
import usersServiceApi from '../../api/UsersServiceAPI';
|
||||||
|
import UserViewForm from '../user-view-form';
|
||||||
|
import {EVENTS, MODES} from '../../consts';
|
||||||
|
import routeService from '../../services/RouteService';
|
||||||
|
import userInfoService from '../../services/UserInfoService';
|
||||||
|
|
||||||
class UsersPage extends Component {
|
class UsersPage extends Component {
|
||||||
constructor (mainNodeSelector, parentNode) {
|
constructor (mainNodeSelector, parentNode) {
|
||||||
super(mainNodeSelector, parentNode);
|
super(mainNodeSelector, parentNode);
|
||||||
|
|
||||||
|
this.usersForm = this.createComponent(UserViewForm);
|
||||||
|
|
||||||
|
this.createElement({tagName: 'div', parentNode: this.mainNode});
|
||||||
|
|
||||||
|
this.createUserButton = this.createElement({
|
||||||
|
tagName: 'button',
|
||||||
|
parentNode: this.mainNode,
|
||||||
|
options: {
|
||||||
|
className: 'btn btn-primary m-3',
|
||||||
|
textContent: 'Создать пользователя',
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
type: 'button',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addSubscribe(userInfoService, EVENTS.CHANGE_USER_INFO, ({is_admin}) => {
|
||||||
|
this.createUserButton.disabled = !is_admin;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addEventListener(this.createUserButton, 'click', () => {
|
||||||
|
routeService.pushQuery({mode: MODES.Create}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addSubscribe(this.usersForm, EVENTS.CREATE_USER, async (user) => {
|
||||||
|
await usersServiceApi.create(user);
|
||||||
|
this.initPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addSubscribe(this.usersForm, EVENTS.SAVE_USER, async (user) => {
|
||||||
|
await usersServiceApi.update(user);
|
||||||
|
this.initPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addSubscribe(this.usersForm, EVENTS.DELETE_USER, async (login) => {
|
||||||
|
await usersServiceApi.remove(login);
|
||||||
|
this.initPage();
|
||||||
|
});
|
||||||
|
|
||||||
this.usersTable = this.createComponent(UsersTable, this.mainNode);
|
this.usersTable = this.createComponent(UsersTable, this.mainNode);
|
||||||
|
|
||||||
|
this.addSubscribe(this.usersTable, EVENTS.ROW_DOUBLE_CLICK, (_, row) => {
|
||||||
|
routeService.pushQuery({
|
||||||
|
mode: MODES.View,
|
||||||
|
login: row.login,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this.initPage();
|
this.initPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
initPage = async () => {
|
initPage = async () => {
|
||||||
|
const user = await userInfoService.getUserInfo();
|
||||||
|
this.createUserButton.disabled = !user.is_admin;
|
||||||
this.userList = await usersServiceApi.request();
|
this.userList = await usersServiceApi.request();
|
||||||
this.renderTable();
|
this.renderTable();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,14 @@ export const EVENTS = {
|
|||||||
CHANGE_USER_AVATAR: 'changeUserAvatar',
|
CHANGE_USER_AVATAR: 'changeUserAvatar',
|
||||||
OPEN_MODAL: 'openModal',
|
OPEN_MODAL: 'openModal',
|
||||||
CLICK: 'click',
|
CLICK: 'click',
|
||||||
SUBMIT: 'submit'
|
SUBMIT: 'submit',
|
||||||
|
FOCUS: 'focus',
|
||||||
|
KEYDOWN: 'keydown',
|
||||||
|
INPUT: 'input',
|
||||||
|
CHANGE: 'change',
|
||||||
|
CREATE_USER: 'createUser',
|
||||||
|
SAVE_USER: 'saveUser',
|
||||||
|
DELETE_USER: 'deleteUser',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FORM_TYPES = {
|
export const FORM_TYPES = {
|
||||||
@ -64,3 +71,17 @@ export const FORM_TYPES = {
|
|||||||
TEXTAREA: 'TEXTAREA',
|
TEXTAREA: 'TEXTAREA',
|
||||||
PASSWORD: 'PASSWORD',
|
PASSWORD: 'PASSWORD',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MODES = {
|
||||||
|
Create: 'create',
|
||||||
|
View: 'view',
|
||||||
|
Edit: 'edit',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TAG_NAME = {
|
||||||
|
OPTION: 'option',
|
||||||
|
DIV: 'div',
|
||||||
|
INPUT: 'input',
|
||||||
|
SELECT: 'select',
|
||||||
|
TEXTAREA: 'textarea',
|
||||||
|
};
|
||||||
|
|||||||
@ -2,21 +2,31 @@ import usersServiceApi from '../api/UsersServiceAPI';
|
|||||||
import {EVENTS} from '../consts';
|
import {EVENTS} from '../consts';
|
||||||
import EmitService from './EmitService';
|
import EmitService from './EmitService';
|
||||||
|
|
||||||
|
const NOT_USER = 'not_user';
|
||||||
|
const DEFAULT_AVATAR = 'https://d5qmjlya0ygtg.cloudfront.net/569/c5295/f9ad/47c8/96a0/66a65609b38d/original/331698.jpg';
|
||||||
class UserInfoService extends EmitService {
|
class UserInfoService extends EmitService {
|
||||||
constructor () {
|
constructor () {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.userInfo = {
|
this.userInfo = {
|
||||||
login: 'not_user',
|
login: NOT_USER,
|
||||||
avatar: 'https://d5qmjlya0ygtg.cloudfront.net/569/c5295/f9ad/47c8/96a0/66a65609b38d/original/331698.jpg',
|
avatar: DEFAULT_AVATAR,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserLogin = async () => {
|
setUserLogin = async () => {
|
||||||
this.userInfo = await usersServiceApi.getMe();
|
this.userInfo = await usersServiceApi.getSelfInfo();
|
||||||
this.next(EVENTS.CHANGE_USER_INFO, {...this.userInfo});
|
this.next(EVENTS.CHANGE_USER_INFO, {...this.userInfo});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUserInfo = async () => {
|
||||||
|
if (this.userInfo.login === NOT_USER) {
|
||||||
|
this.userInfo = await usersServiceApi.getSelfInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {...this.userInfo};
|
||||||
|
}
|
||||||
|
|
||||||
changeAllAvatars () {
|
changeAllAvatars () {
|
||||||
this.next(EVENTS.CHANGE_USER_AVATAR);
|
this.next(EVENTS.CHANGE_USER_AVATAR);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user