Compare commits

...

10 Commits

25 changed files with 369 additions and 100 deletions

View File

@ -58,10 +58,11 @@
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"@popperjs/core": "^2.9.2",
"ace-builds": "^1.4.12",
"axios": "^0.19.2",
"bootstrap": "^5.0.0-alpha1",
"moment": "^2.27.0",
"popper.js": "^1.16.1",
"query-string": "^6.13.1",
"uuid": "^8.2.0"
}

View File

@ -1,6 +1,6 @@
import http from './HttpAPI';
const ROOT_URL = 'http://api.auth.vigdorov.ru/users';
const ROOT_URL = 'https://api.auth.vigdorov.ru/users';
const profileServiceApi = {
getSelfInfo: async () => {

View File

@ -10,11 +10,11 @@ export const API_NAME_HEADER = 'api-name';
export const API_OPTIONS = {
[API_NAMES.PRODUCTION]: {
url: 'http://api.storage.vigdorov.ru',
url: 'https://api.storage.vigdorov.ru',
options: {},
},
[API_NAMES.TESTING]: {
url: 'http://api.storage.vigdorov.ru',
url: 'https://api.storage.vigdorov.ru',
options: {
headers: {
'Api-Name': 'store-service-test',
@ -22,7 +22,7 @@ export const API_OPTIONS = {
},
},
[API_NAMES.DEVELOP]: {
url: 'http://localhost:4001',
url: 'https://localhost:4001',
options: {},
},
};
@ -34,7 +34,7 @@ export const ENDPOINTS = {
HOOKS: '/hooks',
};
export const AUTH_SERVICE = 'http://api.auth.vigdorov.ru';
export const AUTH_SERVICE = 'https://api.auth.vigdorov.ru';
export const AUTH_ENDPOINTS = {
AUTH: '/auth',

View File

@ -32,6 +32,26 @@
</nav>
</template>
<!-- Шаблон для CodeEditor -->
<template id="code-editor">
<div class="CodeEditor">
<div class="CodeEditor__editor form-control"></div>
</div>
</template>
<!-- Шаблон для FormCodeEditor -->
<template id="form-code-editor">
<div class="mb-3 FormCodeEditor">
<label class="form-label">
<span class="FormCodeEditor__label"></span>
<span class="FormCodeEditor__star text-danger"></span>
</label>
<input type="text" class="FormCodeEditor__input" />
<div class="FormCodeEditor__editor"></div>
<div class="FormCodeEditor__errorText form-text invalid-feedback"></div>
</div>
</template>
<!-- Шаблон карточек на главной-->
<template id="main-statistic">
<div class="h-100">
@ -81,10 +101,6 @@
<div class="Login__title h3"></div>
<form class="Login__form needs-validation" novalidate>
<div class="Login__inputContainer"></div>
<div class="form-group form-check Login__check">
<input type="checkbox" class="form-check-input" id="check">
<label class="form-check-label" for="check">Оставаться в системе</label>
</div>
<button type="submit" class="btn btn-primary Login__submit">Войти</button>
</form>
</div>
@ -472,7 +488,7 @@
<!-- Шаблон для Модального Сайдбара -->
<template id="modal-sidebar">
<div class="ModalSidebar row justify-content-end m-0">
<div class="ModalSidebar row justify-content-end m-0 invisible">
<div class="ModalSidebar__window col-12 col-md-9 col-lg-8 col-xl-7 p-0">
<button class="ModalSidebar__close d-flex justify-center align-items-center" type="button">
<i class="fas fa-times"></i>

View File

@ -15,6 +15,7 @@ import authServiceApi from './api/AuthServiceAPI';
import userInfoService from './services/UserInfoService';
import {EVENTS, ROUTES} from './core/consts';
import UsersPage from './pages/users/components/page/Page';
import {DocumentationPage} from './pages/documentation/components/page/Page';
const initAppComponents = () => {
const mainMenu = new MainMenu();
@ -36,10 +37,11 @@ const initAppComponents = () => {
routerPagesContainer.addRoutes([
{url: ROUTES.MAIN, pageComponent: MainPage},
{url: ROUTES.STORE, pageComponent: ApiPage},
{url: ROUTES.LOGS, pageComponent: LogsPage},
{url: ROUTES.LOGS, pageComponent: LogsPage, onlyAdmin: true},
{url: ROUTES.USERS, pageComponent: UsersPage},
{url: ROUTES.LOGIN, pageComponent: LoginPage},
{url: ROUTES.PROFILE, pageComponent: ProfilePage},
{url: ROUTES.DOCUMENTATION, pageComponent: DocumentationPage},
]);
/**

View File

@ -0,0 +1,7 @@
.CodeEditor__editor {
width: 100%;
}
.CodeEditor__editor_disabled {
background-color: #e9ecefa6;
}

View File

@ -0,0 +1,45 @@
import ace from 'ace-builds/src-noconflict/ace';
import theme from 'ace-builds/src-noconflict/theme-github';
import {Mode as JSONMode} from 'ace-builds/src-noconflict/mode-json';
import Component from '../component/Component';
import {CODE_EDITOR_MODE} from '../../consts';
import './CodeEditor.css';
const Modes = {
[CODE_EDITOR_MODE.JSON]: JSONMode,
};
export class CodeEditor extends Component {
constructor ({
parentNode,
id,
mode,
rows = 5,
} = {}) {
super('#code-editor', parentNode);
this.editorContainer = this.mainNode.querySelector('.CodeEditor__editor');
this.editorContainer.setAttribute('id', id);
this.editorContainer.style.minHeight = `${Math.round(rows * 24.33)}px`;
this.editor = ace.edit(id);
this.editor.session.setMode(new Modes[mode]());
this.editor.setTheme(theme);
}
setValue = (value) => {
this.editor.session.setValue(value);
}
getValue = () => {
return this.editor.getValue();
}
disabled = (value) => {
const method = value ? 'add' : 'remove';
this.editorContainer.classList[method]('CodeEditor__editor_disabled');
this.editor.setReadOnly(value);
}
}

View File

@ -0,0 +1,13 @@
.FormCodeEditor {
position: relative;
}
.FormCodeEditor__input {
position: absolute;
z-index: -1000;
}
.FormCodeEditor__input:invalid + .FormCodeEditor__editor {
border: 1px solid var(--bs-red);
border-radius: .25rem;
}

View File

@ -0,0 +1,69 @@
import Component from '../component/Component';
import {CodeEditor} from '../code-editor/CodeEditor';
import './FormCodeEditor.css';
import {EVENTS} from '../../consts';
export class FormCodeEditor extends Component {
constructor (parentNode, {
id,
mode,
label,
required = false,
initValue = '',
className = '',
rows,
} = {}) {
super('#form-code-editor', parentNode);
this.label = this.mainNode.querySelector('.form-label');
this.labelText = this.mainNode.querySelector('.FormCodeEditor__label');
this.star = this.mainNode.querySelector('.FormCodeEditor__star');
this.editorContainer = this.mainNode.querySelector('.FormCodeEditor__editor');
this.errorText = this.mainNode.querySelector('.FormCodeEditor__errorText');
this.input = this.mainNode.querySelector('.FormCodeEditor__input');
this.addEventListener(this.editorContainer, EVENTS.CLICK, this.clearError);
this.addEventListener(this.editorContainer, EVENTS.KEYDOWN, this.clearError);
this.labelText.textContent = label;
this.editor = this.createComponent(CodeEditor, {
parentNode: this.editorContainer,
id,
mode,
rows,
});
if (className) {
this.editorContainer.classList.add(className);
}
if (required) {
this.star.textContent = ' *';
}
this.setValue(initValue);
}
disabled = (value) => {
this.editor.disabled(value);
}
getValue = () => {
return this.editor.getValue();
}
setValue = (value) => {
this.editor.setValue(value);
}
setError = (errorMessage) => {
this.errorText.textContent = errorMessage;
this.input.setCustomValidity(errorMessage);
}
clearError = () => {
this.setError('');
}
}

View File

@ -88,7 +88,7 @@ class FormControl extends Component {
}
clearError = () => {
this.errorText.textContent = '';
this.setError('');
}
getInputTagName = (type) => {

View File

@ -4,6 +4,7 @@ import routeService from '../../../services/RouteService';
import './MainMenu.css';
import {EVENTS, ROUTES, TAG_NAME} from '../../consts';
import tokenApi from '../../../api/TokenAPI';
import userInfoService from '../../../services/UserInfoService';
const NAV_MENU = [
{
@ -17,11 +18,16 @@ const NAV_MENU = [
{
title: 'Журнал',
url: ROUTES.LOGS,
onlyAdmin: true,
},
{
title: 'Пользователи',
url: ROUTES.USERS,
},
{
title: 'Документация',
url: ROUTES.DOCUMENTATION,
},
{
title: 'Личный кабинет',
url: ROUTES.PROFILE,
@ -72,7 +78,7 @@ class MainMenu extends Component {
}
render = () => {
this.menuItems = NAV_MENU.map(({url, title, className = ''}) => {
this.menuItems = NAV_MENU.map(({url, title, className = '', onlyAdmin}) => {
const li = this.createElement({
tagName: TAG_NAME.LI,
parentNode: this.buttonsContainer,
@ -95,7 +101,7 @@ class MainMenu extends Component {
}
});
return {url, link};
return {url, li, link, onlyAdmin};
});
}
@ -107,7 +113,12 @@ class MainMenu extends Component {
this.mainNode.remove();
}
showMenu = () => {
showMenu = async () => {
const {is_admin} = await userInfoService.getUserInfo();
this.menuItems.forEach(({li, onlyAdmin}) => {
const isShow = !onlyAdmin || is_admin;
li.style.display = isShow ? 'list-item' : 'none';
});
document.body.prepend(this.mainNode);
}
}

View File

@ -9,7 +9,7 @@ const HIDE_SHADOW_CLASS = 'ModalSidebar__shadow_hide';
const SHADOW_CLASS = 'ModalSidebar__shadow';
const CROSS_BUTTON_CLASS = 'ModalSidebar__close';
const WINDOW_CLASS = 'ModalSidebar__window';
const WINDOW_INIT_CLASS = 'invisible';
class ModalSidebar extends Component {
constructor ({
content,
@ -56,6 +56,7 @@ class ModalSidebar extends Component {
}
show = () => {
this.mainNode.classList.remove(WINDOW_INIT_CLASS);
this.mainNode.classList.add(SHOW_WINDOW_CLASS);
this.shadow.classList.add(SHOW_SHADOW_CLASS);

View File

@ -3,6 +3,7 @@ import routeService from '../../../services/RouteService';
import NotFoundPage from '../../../pages/not-found/components/page/Page';
import {EVENTS, ROUTES} from '../../consts';
import userInfoService from '../../../services/UserInfoService';
/**
* @interface Route
@ -59,6 +60,14 @@ class RouterPagesContainer extends Component {
this.currentPage = new PageComponent('#page', this.mainNode);
}
});
this.addSubscribe(userInfoService, EVENTS.CHANGE_USER_INFO, ({is_admin}) => {
const {url} = routeService.getUrlData();
const currentRoute = this.routes.find((route) => route.url === url);
if (currentRoute.onlyAdmin && !is_admin) {
routeService.goTo(ROUTES.MAIN);
}
});
}
/**

View File

@ -69,4 +69,9 @@ export const ROUTES = {
USERS: '/users',
LOGIN: '/login',
PROFILE: '/profile',
DOCUMENTATION: '/documentation',
};
export const CODE_EDITOR_MODE = {
JSON: 'json',
};

View File

@ -0,0 +1,16 @@
import Component from '../../../../core/components/component/Component';
import {TAG_NAME} from '../../../../core/consts';
export class DocumentationPage extends Component {
constructor (mainNodeSelector, parentNode) {
super(mainNodeSelector, parentNode);
this.createElement({
tagName: TAG_NAME.DIV,
parentNode: this.mainNode,
options: {
textContent: 'Документация',
},
});
}
}

View File

@ -11,7 +11,6 @@ class LoginForm extends Component {
this.form = this.mainNode.querySelector('.Login__form');
this.inputContainer = this.mainNode.querySelector('.Login__inputContainer');
this.submitButton = this.mainNode.querySelector('.Login__submit');
this.checkboxSystem = this.mainNode.querySelector('.form-check-input');
this.logoImage = this.mainNode.querySelector('.Login__logo');
this.logoImage.src = Image;
@ -39,7 +38,6 @@ class LoginForm extends Component {
this.loginControl.input,
this.passwordControl.input,
this.submitButton,
this.checkboxSystem,
];
elements.forEach((element) => {
@ -61,6 +59,11 @@ class LoginForm extends Component {
return this.form.checkValidity();
}
setError = (message) => {
this.loginControl.setError(message);
this.passwordControl.setError(message);
}
submit = (event) => {
event.preventDefault();
if (this.validateInputs()) {

View File

@ -6,6 +6,11 @@ import routeService from '../../../../services/RouteService';
import notify from '../../../../services/NotifyService';
import {ROUTES, EVENTS} from '../../../../core/consts';
const ERRORS = {
NOT_CORRECT: 'Неверный логин или пароль',
UNKNOWN_ERROR: 'Неизвестная ошибка',
};
class LoginPage extends Component {
constructor (mainNodeSelector, parentNode) {
super(mainNodeSelector, parentNode);
@ -21,8 +26,12 @@ class LoginPage extends Component {
routeService.goTo(ROUTES.MAIN);
})
.catch((e) => {
const message = e?.response?.data?.message || 'Неизвестная ошибка';
notify.warn(message);
const message = e?.response?.data?.message || ERRORS.UNKNOWN_ERROR;
if (message === ERRORS.NOT_CORRECT) {
this.form.setError(ERRORS.NOT_CORRECT);
} else {
notify.warn(message);
}
this.form.disabled(false);
});
});

View File

@ -1,7 +1,7 @@
import Component from '../../../../core/components/component/Component';
import FormControl from '../../../../core/components/form-control/FormControl';
import {FORM_TYPES, EVENTS} from '../../../../core/consts';
import {INPUT_IDS, LABELS} from './consts';
import {INPUT_IDS, LABELS, ERRORS} from './consts';
import notify from '../../../../services/NotifyService';
import profileServiceApi from '../../../../api/ProfileServiceAPI';
@ -48,21 +48,22 @@ class ProfileContent extends Component {
const newPasswordRepeat = this.newPasswordRepeat.getValue();
if (!oldPassword) {
this.oldPassword.setError('Заполните старый пароль');
this.oldPassword.setError(ERRORS.TYPED_OLD_PASSWORD);
}
if (!newPassword) {
this.newPassword.setError('Заполните новый пароль');
this.newPassword.setError(ERRORS.TYPED_NEW_PASSWORD);
}
const isEqualPassword = newPassword === newPasswordRepeat;
if (!isEqualPassword) {
notify.warn('Пароли не совпадают');
this.newPassword.setError(ERRORS.PASSWORD_NOT_EQUAL);
this.newPasswordRepeat.setError(ERRORS.PASSWORD_NOT_EQUAL);
}
if (!newPasswordRepeat) {
this.newPasswordRepeat.setError('Повторите новый пароль');
this.newPasswordRepeat.setError(ERRORS.REPEAT_PASSWORD);
}
return this.form.checkValidity() && isEqualPassword;
@ -80,9 +81,16 @@ class ProfileContent extends Component {
if (this.validateInputs()) {
const oldPassword = this.oldPassword.getValue();
const newPassword = this.newPassword.getValue();
await profileServiceApi.changePassword(oldPassword, newPassword);
try {
await profileServiceApi.changePassword(oldPassword, newPassword);
} catch (e) {
if (e?.response?.data?.message === ERRORS.NOT_CORRECT_PASSWORD) {
this.oldPassword.setError(ERRORS.NOT_CORRECT_PASSWORD);
}
throw new Error(e);
}
this.clearForm();
notify.success('Пароль успешно изменен');
notify.success(LABELS.SUCCESS_CHANGE_PASSWORD);
}
}

View File

@ -11,4 +11,13 @@ export const LABELS = {
OLD_PASSWORD_PLACEHOLDER: 'Введите старый пароль',
NEW_PASSWORD_PLACEHOLDER: 'Введите новый пароль',
NEW_PASSWORD_REPEAT_PLACEHOLDER: 'Введите новый пароль еще раз',
SUCCESS_CHANGE_PASSWORD: 'Пароль изменен',
};
export const ERRORS = {
TYPED_OLD_PASSWORD: 'Заполните старый пароль',
TYPED_NEW_PASSWORD: 'Заполните новый пароль',
PASSWORD_NOT_EQUAL: 'Пароли не совпадают',
REPEAT_PASSWORD: 'Повторите новый пароль',
NOT_CORRECT_PASSWORD: 'Неверный старый пароль',
};

View File

@ -1,6 +1,3 @@
.Api__value-input {
height: 200px;
}
.Api__view-controls {
position: sticky;
bottom: 0;

View File

@ -1,11 +1,14 @@
import Component from '../../../../core/components/component/Component';
import ModalSidebar from '../../../../core/components/modal-sidebar/ModalSibebar';
import FormControl from '../../../../core/components/form-control/FormControl';
import {FORM_TYPES, EVENTS, MODES} from '../../../../core/consts';
import {EVENTS, MODES, CODE_EDITOR_MODE, FORM_TYPES} from '../../../../core/consts';
import './ApiTableViewForm.css';
import storageApi from '../../api/StorageServiceAPI';
import routeService from '../../../../services/RouteService';
import {HooksComponent} from '../hooks-component/HooksComponent';
import {FormCodeEditor} from '../../../../core/components/form-code-editor/FormCodeEditor';
import userInfoService from '../../../../services/UserInfoService';
import usersServiceApi from '../../../users/api/UsersServiceAPI';
const TITLE_MODES = {
[MODES.Create]: 'Создание нового хранилища',
@ -30,37 +33,6 @@ class ApiTableViewForm extends Component {
this.title.textContent = 'Информация о хранилище';
this.inputs = [
this.keyInput = this.createComponent(FormControl, this.form, {
id: 'api-key-input',
label: 'Название хранилища',
pattern: '^[a-zA-Z][a-zA-Z0-9_-]{3,}$',
required: true,
}),
this.serviceNameInput = this.createComponent(FormControl, this.form, {
id: 'api-service-name-input',
label: 'Название сервиса',
required: true,
}),
this.authorInput = this.createComponent(FormControl, this.form, {
id: 'api-author-input',
label: 'Автор хранилища',
required: true,
}),
this.descriptionInput = this.createComponent(FormControl, this.form, {
id: 'api-description-input',
label: 'Краткое описание',
required: true,
}),
this.valueInput = this.createComponent(FormControl, this.form, {
id: 'api-value-input',
type: FORM_TYPES.TEXTAREA,
className: 'Api__value-input',
label: 'Содержимое хранилища',
required: true,
}),
];
this.buttons = [
{
button: this.createButton = this.mainNode.querySelector('.ApiViewForm__create'),
@ -69,10 +41,12 @@ class ApiTableViewForm extends Component {
{
button: this.saveButton = this.mainNode.querySelector('.ApiViewForm__save'),
modes: [MODES.Edit],
onlyOwner: true,
},
{
button: this.editButton = this.mainNode.querySelector('.ApiViewForm__edit'),
modes: [MODES.View],
onlyOwner: true,
},
{
button: this.cancelButton = this.mainNode.querySelector('.ApiViewForm__cancel'),
@ -81,6 +55,7 @@ class ApiTableViewForm extends Component {
{
button: this.deleteButton = this.mainNode.querySelector('.ApiViewForm__delete'),
modes: [MODES.View, MODES.Edit],
onlyOwner: true,
}
];
@ -96,31 +71,74 @@ class ApiTableViewForm extends Component {
this.addEventListener(this.cancelButton, EVENTS.CLICK, this.handleCloseModal);
this.hooksContainer = this.createComponent(HooksComponent, this.mainNode);
this.form.insertAdjacentElement('afterend', this.hooksContainer.mainNode);
this.addSubscribe(routeService, EVENTS.ROUTE_SEARCH_CHANGE, this.setForm);
this.renderInputs();
}
renderInputs = async () => {
const users = await usersServiceApi.request();
this.inputs = [
this.keyInput = this.createComponent(FormControl, this.form, {
id: 'api-key-input',
label: 'Название хранилища',
pattern: '^[a-zA-Z][a-zA-Z0-9_-]{3,}$',
required: true,
}),
this.serviceNameInput = this.createComponent(FormControl, this.form, {
id: 'api-service-name-input',
label: 'Название сервиса',
required: true,
}),
this.authorInput = this.createComponent(FormControl, this.form, {
id: 'api-author-input',
label: 'Автор хранилища',
type: FORM_TYPES.SELECT,
options: users.map((user) => ({
value: user.login,
text: user.login,
})),
}),
this.descriptionInput = this.createComponent(FormControl, this.form, {
id: 'api-description-input',
label: 'Краткое описание',
required: true,
}),
this.valueInput = this.createComponent(FormCodeEditor, this.form, {
id: 'api-value-input',
className: 'Api__value-input',
label: 'Содержимое хранилища',
mode: CODE_EDITOR_MODE.JSON,
required: true,
}),
this.hideSelect = this.createComponent(FormControl, this.form, {
id: 'api-hide-select',
label: 'Видимость хранилища',
type: FORM_TYPES.SELECT,
options: [
{value: true, text: 'Скрыть'},
{value: false, text: 'Показать'},
],
}),
];
this.inputs.forEach((input) => {
input.disabled(true);
});
this.hooksContainer = this.createComponent(HooksComponent, this.mainNode);
this.form.insertAdjacentElement('afterend', this.hooksContainer.mainNode);
this.addSubscribe(routeService, EVENTS.ROUTE_SEARCH_CHANGE, ({query}) => {
const {mode, key} = query;
this.setForm(mode, key);
this.setVisibleHooks(mode);
});
const {query: {mode, key}} = routeService.getUrlData();
if (mode) {
this.setForm(mode, key);
}
this.setForm();
}
handleCloseModal = () => {
routeService.pushQuery({}, true);
}
setVisibleHooks = (mode) => {
const method = mode !== MODES.View ? 'add' : 'remove';
setVisibleHooks = (mode, isAvailable) => {
const isHide = mode !== MODES.View || !isAvailable;
const method = isHide ? 'add' : 'remove';
this.hooksContainer.mainNode.classList[method]('invisible');
}
@ -145,17 +163,26 @@ class ApiTableViewForm extends Component {
};
}
setInputDisabled = (mode) => {
setInputDisabled = (mode, isAdmin) => {
const disabled = !EDIT_MODES.includes(mode);
this.inputs.forEach((input) => {
const disabledLogin = this.keyInput === input && mode === MODES.Edit;
input.disabled(disabled ? disabled : disabledLogin);
if (input === this.keyInput) {
const disabledKey = mode !== MODES.Create;
input.disabled(disabled ? disabled : disabledKey);
return;
}
if (input === this.authorInput) {
const disabledAuthor = mode !== MODES.Create || !isAdmin;
input.disabled(disabled ? disabled : disabledAuthor);
return;
}
input.disabled(disabled);
});
}
showButtons = (mode) => {
this.buttons.forEach(({button, modes}) => {
const isShow = modes.includes(mode);
showButtons = (mode, isAvailable) => {
this.buttons.forEach(({button, modes, onlyOwner}) => {
const isShow = modes.includes(mode) && (onlyOwner ? isAvailable : true);
button.style.display = isShow ? 'inline-block' : 'none';
});
}
@ -201,6 +228,7 @@ class ApiTableViewForm extends Component {
service_name: this.serviceNameInput.getValue(),
author: this.authorInput.getValue(),
description: this.descriptionInput.getValue(),
hide: this.hideSelect.getValue(),
});
this.clearForm();
@ -216,6 +244,7 @@ class ApiTableViewForm extends Component {
service_name: this.serviceNameInput.getValue(),
author: this.authorInput.getValue(),
description: this.descriptionInput.getValue(),
hide: this.hideSelect.getValue(),
});
this.clearForm();
routeService.pushQuery({}, true);
@ -234,17 +263,22 @@ class ApiTableViewForm extends Component {
this.keyInput.setValue(key);
}
setForm = async (mode, storeKey) => {
const {key, value, description, service_name, author} = await this.loadStore(storeKey);
setForm = async () => {
const {query: {mode, key: storeKey}} = routeService.getUrlData();
const {key, value, description, service_name, author, hide} = await this.loadStore(storeKey);
const {login, is_admin} = await userInfoService.getUserInfo();
const isAvailableEdit = author === login || is_admin;
this.setSidebarTitle(mode, storeKey);
this.setKeyInput(mode, key);
this.serviceNameInput.setValue(service_name);
this.authorInput.setValue(author);
this.authorInput.setValue(author || login);
this.descriptionInput.setValue(description);
this.valueInput.setValue(this.stringifyValue(value));
this.hideSelect.setValue(hide);
this.setInputDisabled(mode);
this.showButtons(mode);
this.setInputDisabled(mode, is_admin);
this.showButtons(mode, isAvailableEdit);
this.setVisibleHooks(mode, isAvailableEdit);
if (mode) {
this.sidebar.show();

View File

@ -1,6 +1,6 @@
import http from '../../../api/HttpAPI';
const ROOT_URL = 'http://api.auth.vigdorov.ru/users';
const ROOT_URL = 'https://api.auth.vigdorov.ru/users';
/**
* Объект с полями для создания пользователя

View File

@ -27,7 +27,7 @@ class UsersPage extends Component {
});
this.addSubscribe(userInfoService, EVENTS.CHANGE_USER_INFO, ({is_admin}) => {
this.createUserButton.disabled = !is_admin;
this.setCreateUserButton(is_admin);
});
this.addEventListener(this.createUserButton, EVENTS.CLICK, () => {
@ -61,9 +61,13 @@ class UsersPage extends Component {
this.initPage();
}
setCreateUserButton = (isAdmin) => {
this.createUserButton.style.display = isAdmin ? 'inline-block' : 'none';
}
initPage = async () => {
const user = await userInfoService.getUserInfo();
this.createUserButton.disabled = !user.is_admin;
this.setCreateUserButton(user.is_admin);
this.userList = await usersServiceApi.request();
this.renderTable();
}

View File

@ -85,20 +85,20 @@ class UserViewForm extends Component {
}
];
this.addEventListener(this.cancelButton, 'click', () => {
this.addEventListener(this.cancelButton, EVENTS.CLICK, () => {
routeService.pushQuery({}, true);
});
this.addEventListener(this.editButton, 'click', () => {
this.addEventListener(this.editButton, EVENTS.CLICK, () => {
routeService.pushQuery({mode: MODES.Edit});
});
this.addEventListener(this.form, 'submit', (event) => {
this.addEventListener(this.form, EVENTS.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.addEventListener(this.createButton, EVENTS.CLICK, this.createUser);
this.addEventListener(this.saveButton, EVENTS.CLICK, this.saveUser);
this.addEventListener(this.deleteButton, EVENTS.CLICK, this.deleteUser);
this.addSubscribe(routeService, EVENTS.ROUTE_SEARCH_CHANGE, ({query}) => {
const {mode, login} = query;
@ -128,7 +128,7 @@ class UserViewForm extends Component {
});
}
validateInputs = () => {
validateInputs = (isEditMode) => {
this.form.classList.add('was-validated');
const login = this.login.getValue();
@ -147,6 +147,10 @@ class UserViewForm extends Component {
return '';
})();
if (isEditMode) {
this.password.setError('');
}
this.login.setError(loginErrorMessage);
return this.form.checkValidity();
@ -174,7 +178,7 @@ class UserViewForm extends Component {
}
saveUser = () => {
if (this.validateInputs()) {
if (this.validateInputs(true)) {
this.next(EVENTS.SAVE_USER, {
login: this.login.getValue(),
avatar: this.avatar.getValue(),
@ -214,6 +218,11 @@ class UserViewForm extends Component {
const isShow = mode === MODES.Create;
this.password.setValue('');
this.password.mainNode.style.display = isShow ? 'block' : 'none';
if (isShow) {
this.login.mainNode.after(this.password.mainNode);
} else {
this.mainNode.appendChild(this.password.mainNode);
}
}
setLogin = (mode, login) => {

View File

@ -11,6 +11,7 @@ class UserInfoService extends EmitService {
this.userInfo = {
login: NOT_USER,
avatar: DEFAULT_AVATAR,
is_admin: false,
};
}