HM-133. Рефакторинг (#64)

This commit is contained in:
Nikolay
2020-09-02 20:07:26 +03:00
committed by GitHub
parent caa7671b83
commit bc9b112904
132 changed files with 421 additions and 757 deletions

View File

@ -1,5 +1,5 @@
import {makeUrlWithQuery} from '../utils/urlUtils';
import httpAuthApi from './HttpAuthAPI';
import {makeUrlWithQuery} from '../core/utils';
const GET = 'GET';
const POST = 'POST';

View File

@ -0,0 +1,20 @@
import http from './HttpAPI';
const ROOT_URL = 'http://api.auth.vigdorov.ru/users';
const profileServiceApi = {
getSelfInfo: async () => {
const {data} = await http.get(`${ROOT_URL}/me`);
return data;
},
editSelfInfo: async (user) => {
const {data} = await http.post(`${ROOT_URL}/edit-me`, user);
return data;
},
changePassword: async (old_password, new_password) => {
const {data} = await http.post(`${ROOT_URL}/change-password`, {old_password, new_password});
return data;
},
};
export default profileServiceApi;

View File

@ -1,126 +0,0 @@
import {v4 as uuidv4} from 'uuid';
import storageApi from './StorageServiceAPI';
/**
* @interface StoreListElement
* @type {Object}
* @property {string} _id - уникальный id элемента
*/
/**
* Класс создания api для списков данных
* @class StorageListApi
* @param {string} key - уникальный ключ для api, который будет записан в сервисе
* @param {string} description - описание api
* @param {string} author - автор api
* @param {string} service_name - имя сервиса, для которого используется api
*/
class StorageListApi {
constructor (key, description, author, service_name) {
/**
* Ключ api
* @type {string}
*/
this.key = key;
/**
* @type {StorageServiceApi}
*/
this.api = storageApi;
this.api.create({key, description, author, service_name, value: []});
/**
* @type {Object}
* @property {string} description - описание api
* @property {string} author - автор api
* @property {string} service_name - имя сервиса, для которого используется api
*/
this.metaInfo = {
description, author, service_name
};
}
/**
* @param {StoreListElement[]} list - список элементов по которым осуществялется поиск
* @param {string} _id - _id искомого элемента
* @returns {number} - Возвращает индекс искомого эллемента по _id
*/
_findIndex = (list, _id) => {
return list.findIndex((item) => item._id === _id);
}
/**
* @param {StoreListElement[]} list - новый список элементов
* @returns {Promise<Array<StoreListElement[]>} - возвращает обновленный список элементов
*/
_updateList = async (list) => {
return await this.api.update(this.key, list);
}
/**
* @returns {Promise<StoreListElement[]>} - возвращает все элементы списка
*/
request = async () => {
const data = await this.api.find(this.key);
return (data && data.value) || [];
}
/**
* @param {string} _id - _id искомого элемента списка
* @returns {Promise<StoreListElement>} - возвращает элемент списка или генерит ошибку
*/
find = async (_id) => {
const list = await this.request();
const findIndex = this._findIndex(list, _id);
if (findIndex === -1) {
throw new Error(`Not Found _id: ${_id}`);
}
return list[findIndex];
}
/**
* @param {Object} data - элемент списка
* @returns {Promise<StoreListElement>} - Возвращает вновь созданный элемент с уникальным полем _id
*/
create = async (data) => {
const list = await this.request();
const _id = uuidv4();
const newData = {
...data,
_id,
};
await this._updateList(list.concat(newData));
return newData;
}
/**
* @param {StoreListElement} data - элемент списка
* @returns {Promise<StoreListElement>} - Возвращает обновленный элемент списка
*/
update = async (data) => {
const list = await this.request();
const findIndex = this._findIndex(list, data._id);
if (findIndex === -1) {
throw new Error(`Not Found _id: ${data._id}`);
}
list.splice(findIndex, 1, data);
await this._updateList(list);
return data;
}
/**
* @param {string} _id - _id удаляемого элемента
* @returns {Promise<string>} - Возвращает _id удаленного элемента или ошибку
*/
remove = async (_id) => {
const list = await this.request();
const findIndex = this._findIndex(list, _id);
if (findIndex === -1) {
throw new Error(`Not Found _id: ${_id}`);
}
list.splice(findIndex, 1);
await this._updateList(list);
return _id;
}
}
export default StorageListApi;

View File

@ -6,6 +6,8 @@ export const API_NAMES = {
export const LOCALHOST = 'localhost';
export const API_NAME_HEADER = 'api-name';
export const API_OPTIONS = {
[API_NAMES.PRODUCTION]: {
url: 'http://api.storage.vigdorov.ru',
@ -31,10 +33,6 @@ export const ENDPOINTS = {
SERVER_LOGS: '/logs/server',
};
export const TESTING_HEADERS = {
'Api-Name': 'store-service-test',
};
export const AUTH_SERVICE = 'http://api.auth.vigdorov.ru';
export const AUTH_ENDPOINTS = {

View File

@ -3,18 +3,18 @@ import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap';
import './services/AdminConfigsService';
import ApiPage from './components/api-page';
import MainPage from './components/main-page';
import MainMenu from './components/main-menu/MainMenu';
import ApiPage from './pages/storages/components/page/Page';
import MainPage from './pages/main/components/page/Page';
import MainMenu from './core/components/main-menu/MainMenu';
import routeService from './services/RouteService';
import RouterPagesContainer from './components/router-pages-container/index';
import LogsPage from './components/logs-page/index';
import LoginPage from './components/login-page';
import ProfilePage from './components/profile-page';
import RouterPagesContainer from './core/components/router-pages-container/RouterPagesContainer';
import LogsPage from './pages/logs/components/page/Page';
import LoginPage from './pages/login/components/page/Page';
import ProfilePage from './pages/profile/components/page/Page';
import authServiceApi from './api/AuthServiceAPI';
import userInfoService from './services/UserInfoService';
import {EVENTS} from './consts';
import UsersPage from './components/users-page';
import {EVENTS, ROUTES} from './core/consts';
import UsersPage from './pages/users/components/page/Page';
const initAppComponents = () => {
const mainMenu = new MainMenu();
@ -34,12 +34,12 @@ const initAppComponents = () => {
* ]);
*/
routerPagesContainer.addRoutes([
{url: '/', pageComponent: MainPage},
{url: '/api', pageComponent: ApiPage},
{url: '/logs', pageComponent: LogsPage},
{url: '/users', pageComponent: UsersPage},
{url: '/login', pageComponent: LoginPage},
{url: '/profile', pageComponent: ProfilePage},
{url: ROUTES.MAIN, pageComponent: MainPage},
{url: ROUTES.STORE, pageComponent: ApiPage},
{url: ROUTES.LOGS, pageComponent: LogsPage},
{url: ROUTES.USERS, pageComponent: UsersPage},
{url: ROUTES.LOGIN, pageComponent: LoginPage},
{url: ROUTES.PROFILE, pageComponent: ProfilePage},
]);
/**
@ -58,7 +58,7 @@ const initApp = () => {
initAppComponents();
})
.catch(() => {
history.pushState({}, '', '/login');
history.pushState({}, '', ROUTES.LOGIN);
initAppComponents();
});
};

View File

@ -1,3 +0,0 @@
import ApiInfoComponent from './ApiInfoComponent';
export default ApiInfoComponent;

View File

@ -1,3 +0,0 @@
import ApiPage from './ApiPage';
export default ApiPage;

View File

@ -1,3 +0,0 @@
import ClientLogsTable from './ClientLogsTable';
export default ClientLogsTable;

View File

@ -1,3 +0,0 @@
import ClientLogsViewForm from './ClientLogsViewForm';
export default ClientLogsViewForm;

View File

@ -1,3 +0,0 @@
import Component from './Component';
export default Component;

View File

@ -1,3 +0,0 @@
import CreateApiComponent from './CreateApiComponent';
export default CreateApiComponent;

View File

@ -1,3 +0,0 @@
import FilterInputComponent from './FilterInputComponent';
export default FilterInputComponent;

View File

@ -1,3 +0,0 @@
import FormControl from './FormControl';
export default FormControl;

View File

@ -1,3 +0,0 @@
import HeadersTable from './HeadersTable';
export default HeadersTable;

View File

@ -1,3 +0,0 @@
import LoginForm from './LoginForm';
export default LoginForm;

View File

@ -1,3 +0,0 @@
import LoginPage from './LoginPage';
export default LoginPage;

View File

@ -1,3 +0,0 @@
import LogsFilters from './LogsFilters';
export default LogsFilters;

View File

@ -1,3 +0,0 @@
import LogsPage from './LogsPage';
export default LogsPage;

View File

@ -1,3 +0,0 @@
import MainMenu from './MainMenu';
export default MainMenu;

View File

@ -1,3 +0,0 @@
import MainPage from './MainPage';
export default MainPage;

View File

@ -1,3 +0,0 @@
import MainStatistic from './MainStatistic';
export default MainStatistic;

View File

@ -1,3 +0,0 @@
import ModalSidebar from './ModalSibebar';
export default ModalSidebar;

View File

@ -1,3 +0,0 @@
import Modal from './Modal';
export default Modal;

View File

@ -1,3 +0,0 @@
import NotFoundContent from './NotFoundContent';
export default NotFoundContent;

View File

@ -1,3 +0,0 @@
import NotFoundPage from './NotFoundPage';
export default NotFoundPage;

View File

@ -1,3 +0,0 @@
import NotifyContainer from './NotifyContainer';
export default NotifyContainer;

View File

@ -1,3 +0,0 @@
import NotifyMessage from './NotifyMessage';
export default NotifyMessage;

View File

@ -1,3 +0,0 @@
import Pagination from './Pagination';
export default Pagination;

View File

@ -1,3 +0,0 @@
import ProfileContent from './ProfileContent';
export default ProfileContent;

View File

@ -1,3 +0,0 @@
import ProfilePage from './ProfilePage';
export default ProfilePage;

View File

@ -1,3 +0,0 @@
import RouterPagesContainer from './RouterPagesContainer';
export default RouterPagesContainer;

View File

@ -1,3 +0,0 @@
import FilterApiComponent from './FilterApiComponent';
export default FilterApiComponent;

View File

@ -1,3 +0,0 @@
import ServerLogsTable from './ServerLogsTable';
export default ServerLogsTable;

View File

@ -1,3 +0,0 @@
import ServerLogsViewForm from './ServerLogsViewForm';
export default ServerLogsViewForm;

View File

@ -1,3 +0,0 @@
import TableCellOverflow from './TableCellOverflow';
export default TableCellOverflow;

View File

@ -1,23 +0,0 @@
import Component from '../component/index';
class TableColumnComponent extends Component {
constructor (tableHeadRow, text) {
super('#table-column', tableHeadRow);
this.row = this.mainNode;
this.columnName = {
key: 'Название хранилища',
description: 'Описание',
service_name: 'Имя сервиса',
author: 'Автор',
value: 'Тип содержимого'
};
this.row.textContent = this.columnName[text];
this.addEventListener(this.mainNode, 'click', (evt) => {
this.next('click', evt);
});
}
}
export default TableColumnComponent;

View File

@ -1,3 +0,0 @@
import TableColumnComponent from './TableColumnComponent';
export default TableColumnComponent;

View File

@ -1,48 +0,0 @@
import Component from '../component/index';
import TableColumnComponent from '../table-column-component/index';
import TableRowComponent from '../table-row-component/TableRowComponent';
class TableComponent extends Component {
constructor (parentNode) {
super('#main-table', parentNode);
this.tableHead = this.mainNode.querySelector('.Table__head');
this.tableBody = this.mainNode.querySelector('.Table__body');
this.tableHeadRow = this.mainNode.querySelector('.Table__head-row');
this.columnArr = [];
this.rowArr = [];
}
render = (array) => {
this.columnArr.forEach((item) => {
item.destroy();
});
this.rowArr.forEach((item) => {
item.destroy();
});
this.array = array;
this.columns = Object.keys(array[0]);
this.columns.forEach((item, index) => {
const columnElement = this.createComponent(TableColumnComponent, this.tableHeadRow, item, index);
columnElement.subscribe('click', () => {
this.sort(item, array);
});
this.columnArr.push(columnElement);
});
this.array.forEach((item, index) => {
const newRow = this.createComponent(TableRowComponent, this.tableBody, item, index);
newRow.subscribe('dblclick', () => {
this.next('showInfo', item);
});
this.rowArr.push(newRow);
});
}
sort = (item, array) => {
this.array.sort((a, b) => a[item] > b[item]);
this.render(array);
}
}
export default TableComponent;

View File

@ -1,3 +0,0 @@
import TableComponent from './TableComponent';
export default TableComponent;

View File

@ -1,31 +0,0 @@
import Component from '../component/index';
class TableRowComponent extends Component {
constructor (container, object, index) {
super('#table-row', container);
this.row = this.mainNode;
this.indexPlace = this.mainNode.querySelector('.Body__row-index');
this.content = Object.values(object);
this.indexPlace.textContent = index + 1;
this.content.forEach((text) => {
const contentPlace = document.createElement('td');
if (typeof(text) !== 'string') {
contentPlace.textContent = typeof(text);
} else if (text.length > 30) {
contentPlace.textContent = `${text.substr(0, 30)} ...`;
} else {
contentPlace.textContent = text;
}
this.row.appendChild(contentPlace);
});
this.addEventListener(this.mainNode, 'dblclick', (evt) => {
this.next('dblclick', evt);
});
}
}
export default TableRowComponent;

View File

@ -1,3 +0,0 @@
import Table from './Table';
export default Table;

View File

@ -1,3 +0,0 @@
import UserViewForm from './UserViewForm';
export default UserViewForm;

View File

@ -1,3 +0,0 @@
import UsersPage from './UsersPage';
export default UsersPage;

View File

@ -1,3 +0,0 @@
import UsersTable from './UsersTable';
export default UsersTable;

View File

@ -1,98 +0,0 @@
export const LOG_TYPE = {
SERVER: 'server-logs',
CLIENT: 'client-logs',
};
export const LOG_LABELS = [
{id: LOG_TYPE.SERVER, label: 'Ошибки сервера'},
{id: LOG_TYPE.CLIENT, label: 'Ошибки запросов'},
];
export const SERVER_COLS = [
{id: '_id', label: 'id', width: '240px'},
{id: 'date', label: 'Дата', width: '150px'},
{id: 'type', label: 'Тип записи', width: '120px'},
{id: 'message', label: 'Сообщение', width: '200px'},
{id: 'trace', label: 'Стек', width: '200px'},
];
export const CLIENT_COLS = [
{id: 'startTime', label: 'Дата запроса', width: '150px'},
{id: 'type', label: 'Результат', width: '100px'},
{id: 'data_base', label: 'База данных'},
{id: 'url', label: 'URL'},
{id: 'method', label: 'Метод'},
{id: 'status', label: 'Код ответа'},
{id: 'message', label: 'Сообщение'},
{id: 'duration', label: 'Скорость ответа'},
];
export const LOG_COLS = {
[LOG_TYPE.SERVER]: SERVER_COLS,
[LOG_TYPE.CLIENT]: CLIENT_COLS,
};
export const USERS_COLS = [
{id: 'login', label: 'Логин'},
{id: 'avatar', label: 'Аватар'},
{id: 'is_admin', label: 'Админ'},
];
export const API_COLS = [
{id: 'index', label: '#'},
{id: 'key', label: 'Название хранилища'},
{id: 'description', label: 'Описание'},
{id: 'service_name', label: 'Имя сервиса'},
{id: 'author', label: 'Автор'},
];
export const EVENTS = {
ROUTE_CHANGE: 'routeChange',
ROUTE_SEARCH_CHANGE: 'routeSearchChange',
ROW_CLICK: 'rowClick',
ROW_DOUBLE_CLICK: 'rowDoubleClick',
CHANGE_USER_INFO: 'changeUserInfo',
CHANGE_USER_AVATAR: 'changeUserAvatar',
OPEN_MODAL: 'openModal',
CLICK: 'click',
SUBMIT: 'submit',
FOCUS: 'focus',
KEYDOWN: 'keydown',
INPUT: 'input',
CHANGE: 'change',
CREATE_USER: 'createUser',
SAVE_USER: 'saveUser',
DELETE_USER: 'deleteUser',
};
export const FORM_TYPES = {
TEXT: 'TEXT',
SELECT: 'SELECT',
TEXTAREA: 'TEXTAREA',
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',
LABEL: 'label',
};
export const TABLE_TYPE = {
DEFAULT: 'table table-striped table-hover',
BORDERED: 'table table-bordered table-hover',
};
export const CAPTION_POSITION = {
TOP: 'caption-top',
BOTH: '',
};

View File

@ -1,4 +1,4 @@
import {makeUrlWithQuery} from '../urlUtils';
import {makeUrlWithQuery} from '../utils';
describe('Проверка функции makeUrlWithQuery', () => {
it('Передача корректного маршрута без query', () => {

View File

@ -1,4 +1,5 @@
import Component from '../component/index';
import Component from '../component/Component';
import {EVENTS} from '../../consts';
class ButtonComponent extends Component {
constructor (parentNode, text, newStyle) {
@ -7,8 +8,8 @@ class ButtonComponent extends Component {
this.mainNode.textContent = text;
this.mainNode.className = newStyle;
this.addEventListener(this.mainNode, 'click', (event) => {
this.next('click', event);
this.addEventListener(this.mainNode, EVENTS.CLICK, (event) => {
this.next(EVENTS.CLICK, event);
});
}
}

View File

@ -1,5 +1,5 @@
import EmitService from '../../services/EmitService';
import {createElement} from '../../utils/elementUtils';
import EmitService from '../../../services/EmitService';
import {createElement} from '../../utils';
/**
* Класс для создания компонентов приложения. Необходим для наследования.

View File

@ -1,4 +1,4 @@
import Component from '../component';
import Component from '../component/Component';
import {FORM_TYPES, EVENTS, TAG_NAME} from '../../consts';
class FormControl extends Component {

View File

@ -1,30 +1,30 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
import Component from '../component/Component';
import routeService from '../../../services/RouteService';
import './MainMenu.css';
import {EVENTS} from '../../consts';
import tokenApi from '../../api/TokenAPI';
import {EVENTS, ROUTES, TAG_NAME} from '../../consts';
import tokenApi from '../../../api/TokenAPI';
export const NAV_MENU = [
const NAV_MENU = [
{
title: 'Главная',
url: '/',
url: ROUTES.MAIN,
},
{
title: 'Список хранилищ',
url: '/api',
url: ROUTES.STORE,
},
{
title: 'Журнал',
url: '/logs',
url: ROUTES.LOGS,
},
{
title: 'Пользователи',
url: '/users',
url: ROUTES.USERS,
},
{
title: 'Личный кабинет',
url: '/profile',
url: ROUTES.PROFILE,
className: 'MainMenu__profileButton'
},
];
@ -41,14 +41,14 @@ class MainMenu extends Component {
this.exitButton = this.mainNode.querySelector('.MainMenu__exitButton');
this.collapse = this.mainNode.querySelector('.navbar-collapse');
this.addEventListener(this.exitButton, 'click', this.exitApp);
this.addEventListener(this.exitButton, EVENTS.CLICK, this.exitApp);
this.addEventListener(this.logoBox, 'click', () => {
routeService.goTo('/');
this.addEventListener(this.logoBox, EVENTS.CLICK, () => {
routeService.goTo(ROUTES.MAIN);
});
this.addEventListener(this.avatar, 'click', () => {
routeService.goTo('/profile');
this.addEventListener(this.avatar, EVENTS.CLICK, () => {
routeService.goTo(ROUTES.PROFILE);
});
this.addSubscribe(routeService, EVENTS.ROUTE_CHANGE, (route) => {
@ -74,21 +74,21 @@ class MainMenu extends Component {
render = () => {
this.menuItems = NAV_MENU.map(({url, title, className = ''}) => {
const li = this.createElement({
tagName: 'li',
tagName: TAG_NAME.LI,
parentNode: this.buttonsContainer,
options: {
className: `nav-item ${className}`,
},
});
const link = this.createElement({
tagName: 'a',
tagName: TAG_NAME.A,
parentNode: li,
options: {
className: 'nav-link',
textContent: title,
},
});
this.addEventListener(li, 'click', () => {
this.addEventListener(li, EVENTS.CLICK, () => {
routeService.goTo(url);
if (this.collapse.classList.contains('show')) {
this.collapse.classList.remove('show');

View File

@ -1,10 +1,14 @@
import Component from '../component';
import Component from '../component/Component';
import './ModalSidebar.css';
import {TAG_NAME, EVENTS} from '../../consts';
const SHOW_WINDOW_CLASS = 'ModalSidebar__window_show';
const HIDE_WINDOW_CLASS = 'ModalSidebar__window_hide';
const SHOW_SHADOW_CLASS = 'ModalSidebar__shadow_show';
const HIDE_SHADOW_CLASS = 'ModalSidebar__shadow_hide';
const SHADOW_CLASS = 'ModalSidebar__shadow';
const CROSS_BUTTON_CLASS = 'ModalSidebar__close';
const WINDOW_CLASS = 'ModalSidebar__window';
class ModalSidebar extends Component {
constructor ({
@ -15,28 +19,28 @@ class ModalSidebar extends Component {
super('#modal-sidebar', parentNode);
this.shadow = this.createElement({
tagName: 'div',
tagName: TAG_NAME.DIV,
parentNode,
options: {
className: 'ModalSidebar__shadow',
className: SHADOW_CLASS,
},
});
this.crossButton = this.mainNode.querySelector('.ModalSidebar__close');
this.crossButton = this.mainNode.querySelector(`.${CROSS_BUTTON_CLASS}`);
this.window = this.mainNode.querySelector('.ModalSidebar__window');
this.window = this.mainNode.querySelector(`.${WINDOW_CLASS}`);
this.window.appendChild(content);
if (!disabledShadowClose) {
this.addEventListener(this.mainNode, 'click', (event) => {
this.addEventListener(this.mainNode, EVENTS.CLICK, (event) => {
if (event.target === this.mainNode) {
this.hide();
}
});
}
this.addEventListener(this.crossButton, 'click', this.hide);
this.addEventListener(this.crossButton, EVENTS.CLICK, this.hide);
}
isOpen = () => {

View File

@ -1,4 +1,4 @@
import Component from '../component/index';
import Component from '../component/Component';
import './Modal.css';

View File

@ -1,4 +1,4 @@
import Component from '../component';
import Component from '../component/Component';
import './NotifyContainer.css';
class NotifyContainer extends Component {

View File

@ -1,6 +1,7 @@
import Component from '../component';
import notifyContainer from '../notify-container';
import Component from '../component/Component';
import notifyContainer from '../notify-container/NotifyContainer';
import './NotifyMessage.css';
import {EVENTS} from '../../consts';
class NotifyMessage extends Component {
constructor ({
@ -29,7 +30,7 @@ class NotifyMessage extends Component {
if (autohide) {
this.closeButton.remove();
} else {
this.addEventListener(this.closeButton, 'click', () => {
this.addEventListener(this.closeButton, EVENTS.CLICK, () => {
this.mainNode.remove();
});
}

View File

@ -1,7 +1,7 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
import {prepareToNumber} from '../../utils/urlUtils';
import Component from '../component/Component';
import routeService from '../../../services/RouteService';
import {EVENTS} from '../../consts';
import {prepareToNumber} from '../../utils';
const LEFT_ICON = '&laquo;';
const RIGHT_ICON = '&raquo;';
@ -44,7 +44,7 @@ class Pagination extends Component {
if (text === this.currentPage) {
li.classList.add('active');
}
this.addEventListener(button, 'click', () => {
this.addEventListener(button, EVENTS.CLICK, () => {
const nextPage = (() => {
if (text === LEFT_ICON) {
return this.currentPage - 1;

View File

@ -1,8 +1,8 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
import NotFoundPage from '../not-found-page';
import Component from '../component/Component';
import routeService from '../../../services/RouteService';
import NotFoundPage from '../../../pages/not-found/components/page/Page';
import {EVENTS} from '../../consts';
import {EVENTS, ROUTES} from '../../consts';
/**
* @interface Route
@ -42,7 +42,7 @@ class RouterPagesContainer extends Component {
}) || {};
// Показывает или прячет меню в зависимости от роут
if (['/login'].includes(url)) {
if ([ROUTES.LOGIN].includes(url)) {
mainMenu.hideMenu();
} else {
mainMenu.showMenu();

View File

@ -1,24 +1,25 @@
import Component from '../component';
import Component from '../component/Component';
import './TableCellOverflow.css';
import {TAG_NAME} from '../../consts';
class TableCellOverflow extends Component {
constructor (parentNode, text) {
super(null, parentNode);
const cell = this.createElement({
tagName: 'td',
tagName: TAG_NAME.TD,
parentNode: this.mainNode,
});
const div = this.createElement({
tagName: 'div',
tagName: TAG_NAME.DIV,
parentNode: cell,
options: {
className: 'TableCellOverflow__cellWrapper'
}
});
const span = this.createElement({
tagName: 'span',
tagName: TAG_NAME.SPAN,
parentNode: div,
options: {
className: 'TableCellOverflow__cell',

View File

@ -1,4 +1,4 @@
import Component from '../component/index';
import Component from '../component/Component';
class HeaderCol extends Component {
constructor (parentNode, col) {

View File

@ -1,11 +1,12 @@
import Component from '../component';
import Component from '../component/Component';
import {TAG_NAME} from '../../consts';
class RowCol extends Component {
constructor (parentNode, text) {
super(null, parentNode);
this.createElement({
tagName: 'td',
tagName: TAG_NAME.TD,
parentNode: this.mainNode,
options: {
innerHTML: text,

View File

@ -1,4 +1,4 @@
import Component from '../component/index';
import Component from '../component/Component';
import HeaderCol from './HeaderCol';
import TableRow from './TableRow';
import TableRowWrapper from './TableRowWrapper';

View File

@ -1,4 +1,4 @@
import Component from '../component/index';
import Component from '../component/Component';
import RowCol from './RowCol';
class TableRow extends Component {

View File

@ -1,15 +1,15 @@
import Component from '../component';
import Component from '../component/Component';
import {EVENTS} from '../../consts';
class TableRowWrapper extends Component {
constructor (parentNode) {
super('#uni-table-row', parentNode);
this.addEventListener(this.mainNode, 'click', () => {
this.addEventListener(this.mainNode, EVENTS.CLICK, () => {
this.next(EVENTS.ROW_CLICK);
});
this.addEventListener(this.mainNode, 'dblclick', () => {
this.addEventListener(this.mainNode, EVENTS.DBL_CLICK, () => {
this.next(EVENTS.ROW_DOUBLE_CLICK);
});
}

64
src/core/consts.js Normal file
View File

@ -0,0 +1,64 @@
export const EVENTS = {
ROUTE_CHANGE: 'routeChange',
ROUTE_SEARCH_CHANGE: 'routeSearchChange',
ROW_CLICK: 'rowClick',
ROW_DOUBLE_CLICK: 'rowDoubleClick',
CHANGE_USER_INFO: 'changeUserInfo',
CHANGE_USER_AVATAR: 'changeUserAvatar',
OPEN_MODAL: 'openModal',
CLICK: 'click',
DBL_CLICK: 'dblclick',
SUBMIT: 'submit',
FOCUS: 'focus',
KEYDOWN: 'keydown',
INPUT: 'input',
CHANGE: 'change',
CREATE_USER: 'createUser',
SAVE_USER: 'saveUser',
DELETE_USER: 'deleteUser',
};
export const FORM_TYPES = {
TEXT: 'TEXT',
SELECT: 'SELECT',
TEXTAREA: 'TEXTAREA',
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',
LABEL: 'label',
LI: 'li',
A: 'a',
TD: 'td',
SPAN: 'span',
};
export const TABLE_TYPE = {
DEFAULT: 'table table-striped table-hover',
BORDERED: 'table table-bordered table-hover',
};
export const CAPTION_POSITION = {
TOP: 'caption-top',
BOTH: '',
};
export const ROUTES = {
MAIN: '/',
STORE: '/store',
LOGS: '/logs',
USERS: '/users',
LOGIN: '/login',
PROFILE: '/profile',
};

View File

@ -1,5 +1,35 @@
import moment from 'moment';
import toString from 'lodash/toString';
import {stringify} from 'querystring';
import toNumber from 'lodash/toNumber';
import {API_NAME_HEADER} from '../api/consts';
export const makeApiName = (headers) => {
return headers[API_NAME_HEADER] ? 'testing' : 'production';
};
export const makeDurationMessage = (startTime, endTime, format) => {
const duration = moment(endTime, format).millisecond() - moment(startTime, format).millisecond();
return `${duration} мс`;
};
export const prepareClientLogElement = (client) => {
const {request, response} = client;
const {headers, method, url} = request;
const {status, message} = response;
const data_base = makeApiName(headers);
return {
...client,
data_base,
method,
url,
status: status || 500,
message: message || 'Критическая ошибка сервера',
duration: makeDurationMessage(client.startTime, client.endTime),
};
};
/**
* @interface CreateElementProps
@ -59,3 +89,30 @@ export const prepareServerDate = (stringDate) => {
export const prepareObjectToString = (object) => {
return JSON.stringify(object);
};
/**
* Из маршрута и объекта query создает строку для url
* @param {string} url - маршрут
* @param {Object<string, string>} query - объект с данными
*/
export const makeUrlWithQuery = (url = '', query = {}) => {
const stringQuery = stringify(query);
return url + (stringQuery ? `?${stringQuery}` : '');
};
/**
* Преобразует текстовое значение из url'a в номер страницы. Если не получается, то возвращает 1 страницу.
* @param {unknown} number - значение из url'a, которое мы хотим превратить в номер страницы
* @param {number} countPages - общее количество страниц
*/
export const prepareToNumber = (number, countPages) => {
const prepare = toNumber(number);
const prepareNaN = Number.isNaN(prepare) ? 1 : prepare;
if (prepareNaN < 1) {
return 1;
}
if (prepareNaN > countPages) {
return countPages;
}
return prepareNaN;
};

View File

@ -1,7 +1,7 @@
import Component from '../component';
import Image from '../../img/logo.svg';
import FormControl from '../form-control';
import {FORM_TYPES} from '../../consts';
import Component from '../../../../core/components/component/Component';
import Image from '../../../../img/logo.svg';
import FormControl from '../../../../core/components/form-control/FormControl';
import {FORM_TYPES} from '../../../../core/consts';
class LoginForm extends Component {
constructor (parentNode) {

View File

@ -1,9 +1,10 @@
import Component from '../component/index';
import './LoginPage.css';
import LoginForm from '../login-form';
import authServiceApi from '../../api/AuthServiceAPI';
import routeService from '../../services/RouteService';
import notify from '../../services/NotifyService';
import Component from '../../../../core/components/component/Component';
import './Page.css';
import LoginForm from '../login-form/LoginForm';
import authServiceApi from '../../../../api/AuthServiceAPI';
import routeService from '../../../../services/RouteService';
import notify from '../../../../services/NotifyService';
import {ROUTES, EVENTS} from '../../../../core/consts';
class LoginPage extends Component {
constructor (mainNodeSelector, parentNode) {
@ -11,13 +12,13 @@ class LoginPage extends Component {
this.form = this.createComponent(LoginForm, this.mainNode);
this.addSubscribe(this.form, 'submit', ({login, password}) => {
this.addSubscribe(this.form, EVENTS.SUBMIT, ({login, password}) => {
this.form.disabled(true);
authServiceApi.auth(login, password)
.then(() => {
this.form.disabled(false);
notify.success('Вы авторизованы');
routeService.goTo('/');
routeService.goTo(ROUTES.MAIN);
})
.catch((e) => {
const message = e?.response?.data?.message || 'Неизвестная ошибка';

View File

@ -1,6 +1,6 @@
import {ENDPOINTS} from './consts';
import adminConfigsService from '../services/AdminConfigsService';
import http from './HttpAPI';
import {ENDPOINTS} from '../../../api/consts';
import adminConfigsService from '../../../services/AdminConfigsService';
import http from '../../../api/HttpAPI';
class StorageLogsApi {
constructor () {

View File

@ -1,6 +1,6 @@
import Table from '../table';
import {LOG_COLS, LOG_TYPE} from '../../consts';
import Table from '../../../../core/components/table/Table';
import ClientLogsTableRow from './ClientLogsTableRow';
import {LOG_COLS, LOG_TYPE} from '../../consts';
class ClientLogsTable extends Table {
constructor () {

View File

@ -1,5 +1,5 @@
import Component from '../component';
import TableCellOverflow from '../table-cell-overflow';
import Component from '../../../../core/components/component/Component';
import TableCellOverflow from '../../../../core/components/table-cell-overflow/TableCellOverflow';
class ClientLogsTableRow extends Component {
constructor (parentNode, cols, row) {

View File

@ -1,11 +1,11 @@
import Component from '../component';
import FormControl from '../form-control';
import {FORM_TYPES, TAG_NAME} from '../../consts';
import ModalSidebar from '../modal-sidebar';
import Component from '../../../../core/components/component/Component';
import FormControl from '../../../../core/components/form-control/FormControl';
import {FORM_TYPES, TAG_NAME} from '../../../../core/consts';
import ModalSidebar from '../../../../core/components/modal-sidebar/ModalSibebar';
import './ClientLogsViewForm.css';
import {INPUT_IDS, LABELS, CLASSNAMES} from './consts';
import {makeApiName, makeDurationMessage} from '../../utils/converters';
import HeadersTable from '../headers-table';
import HeadersTable from '../headers-table/HeadersTable';
import {makeDurationMessage, makeApiName} from '../../../../core/utils';
const FORMAT = 'DD/MM/YYYY HH:mm:ss';

View File

@ -1,5 +1,5 @@
import Component from '../component';
import TableCellOverflow from '../table-cell-overflow';
import Component from '../../../../core/components/component/Component';
import TableCellOverflow from '../../../../core/components/table-cell-overflow/TableCellOverflow';
class HeaderTableRow extends Component {
constructor (parentNode, cols, row) {

View File

@ -1,6 +1,6 @@
import Table from '../table';
import Table from '../../../../core/components/table/Table';
import HeaderTableRow from './HeaderTableRow';
import {TABLE_TYPE} from '../../consts';
import {TABLE_TYPE} from '../../../../core/consts';
const COLUMNS = [
{id: 'header', label: 'Заголовок'},

View File

@ -1,7 +1,7 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
import {LOG_TYPE, LOG_LABELS} from '../../consts';
import Component from '../../../../core/components/component/Component';
import routeService from '../../../../services/RouteService';
import './LogsFilters.css';
import {LOG_TYPE, LOG_LABELS} from '../../consts';
class LogsFilters extends Component {
constructor (parentNode) {

View File

@ -1,17 +1,16 @@
import toString from 'lodash/toString';
import Component from '../component/index';
import Component from '../../../../core/components/component/Component';
import storageLogsApi from '../../api/StorageLogsAPI';
import Pagination from '../pagination';
import LogsFilters from '../logs-filters';
import routeService from '../../services/RouteService';
import {prepareToNumber} from '../../utils/urlUtils';
import {LOG_TYPE, EVENTS} from '../../consts';
import {markText, prepareServerDate, prepareObjectToString} from '../../utils/elementUtils';
import ServerLogsTable from '../server-logs-table';
import ClientLogsTable from '../client-logs-table';
import ClientLogsViewForm from '../client-logs-view-form';
import ServerLogsViewForm from '../server-logs-view-form';
import {prepareClientLogElement} from '../../utils/converters';
import Pagination from '../../../../core/components/pagination/Pagination';
import LogsFilters from '../logs-filters/LogsFilters';
import routeService from '../../../../services/RouteService';
import {EVENTS} from '../../../../core/consts';
import ServerLogsTable from '../server-logs-table/ServerLogsTable';
import ClientLogsTable from '../client-logs-table/ClientLogsTable';
import ClientLogsViewForm from '../client-logs-view-form/ClientLogsViewForm';
import ServerLogsViewForm from '../server-logs-view-form/ServerLogsViewForm';
import {prepareClientLogElement, markText, prepareToNumber, prepareServerDate, prepareObjectToString} from '../../../../core/utils';
import {LOG_TYPE} from '../../consts';
const ELEMENTS_ON_PAGE = 15;

View File

@ -1,6 +1,6 @@
import Table from '../table';
import {LOG_TYPE, LOG_COLS} from '../../consts';
import Table from '../../../../core/components/table/Table';
import ServerLogsTableRow from './ServerLogsTableRow';
import {LOG_COLS, LOG_TYPE} from '../../consts';
class ServerLogsTable extends Table {
constructor () {

View File

@ -1,5 +1,5 @@
import Component from '../component';
import TableCellOverflow from '../table-cell-overflow';
import Component from '../../../../core/components/component/Component';
import TableCellOverflow from '../../../../core/components/table-cell-overflow/TableCellOverflow';
class ServerLogsTableRow extends Component {
constructor (parentNode, cols, row) {

View File

@ -1,7 +1,7 @@
import Component from '../component';
import ModalSidebar from '../modal-sidebar';
import FormControl from '../form-control';
import {FORM_TYPES} from '../../consts';
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} from '../../../../core/consts';
import './ServerLogsViewForm.css';
class ServerLogsViewForm extends Component {

33
src/pages/logs/consts.js Normal file
View File

@ -0,0 +1,33 @@
export const LOG_TYPE = {
SERVER: 'server-logs',
CLIENT: 'client-logs',
};
export const LOG_LABELS = [
{id: LOG_TYPE.SERVER, label: 'Ошибки сервера'},
{id: LOG_TYPE.CLIENT, label: 'Ошибки запросов'},
];
const SERVER_COLS = [
{id: '_id', label: 'id', width: '240px'},
{id: 'date', label: 'Дата', width: '150px'},
{id: 'type', label: 'Тип записи', width: '120px'},
{id: 'message', label: 'Сообщение', width: '200px'},
{id: 'trace', label: 'Стек', width: '200px'},
];
const CLIENT_COLS = [
{id: 'startTime', label: 'Дата запроса', width: '150px'},
{id: 'type', label: 'Результат', width: '100px'},
{id: 'data_base', label: 'База данных'},
{id: 'url', label: 'URL'},
{id: 'method', label: 'Метод'},
{id: 'status', label: 'Код ответа'},
{id: 'message', label: 'Сообщение'},
{id: 'duration', label: 'Скорость ответа'},
];
export const LOG_COLS = {
[LOG_TYPE.SERVER]: SERVER_COLS,
[LOG_TYPE.CLIENT]: CLIENT_COLS,
};

View File

@ -1,5 +1,5 @@
import Component from '../component/index';
import MainStatistic from '../main-statistic';
import Component from '../../../../core/components/component/Component';
import MainStatistic from '../statistic/Statistic';
class MainPage extends Component {
constructor (mainNodeSelector, parentNode) {

View File

@ -1,6 +1,6 @@
import Component from '../component/index';
import './MainStatistic.css';
import storageApi from '../../api/StorageServiceAPI';
import Component from '../../../../core/components/component/Component';
import './Statistic.css';
import storageApi from '../../../storages/api/StorageServiceAPI';
class MainStatistic extends Component {
constructor (parentNode) {

View File

@ -1,4 +1,4 @@
import Component from '../component/Component';
import Component from '../../../../core/components/component/Component';
class NotFoundContent extends Component {
constructor (parentNode) {

View File

@ -1,7 +1,8 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
import NotFoundContent from '../not-found-content';
import './NotFoundPage.css';
import Component from '../../../../core/components/component/Component';
import routeService from '../../../../services/RouteService';
import NotFoundContent from '../not-found-content/NotFoundContent';
import './Page.css';
import {ROUTES, EVENTS} from '../../../../core/consts';
class NotFoundPage extends Component {
constructor (mainNodeSelector, parentNode) {
@ -10,8 +11,8 @@ class NotFoundPage extends Component {
this.mainNodeSelector = this.mainNode.querySelector('.NotFound__text');
this.mainNodeSelector.textContent = `Запрашиваемая Вами страница ${location.pathname} не найдена`;
this.redirectButton = this.mainNode.querySelector('.NotFound__redirectButton');
this.addEventListener(this.redirectButton, 'click', () => {
routeService.goTo('/');
this.addEventListener(this.redirectButton, EVENTS.CLICK, () => {
routeService.goTo(ROUTES.MAIN);
});
}
}

View File

@ -1,17 +1,16 @@
import Modal from '../modal/Modal';
import {createElement} from '../../utils/elementUtils';
import FormControl from '../form-control/index';
import Modal from '../../../../core/components/modal/Modal';
import FormControl from '../../../../core/components/form-control/FormControl';
import './AvatarModal.css';
import usersServiceApi from '../../api/UsersServiceAPI';
import userInfoService from '../../services/UserInfoService';
import {EVENTS} from '../../consts';
import userInfoService from '../../../../services/UserInfoService';
import {EVENTS} from '../../../../core/consts';
import profileServiceApi from '../../../../api/ProfileServiceAPI';
class AvatarModal extends Modal {
constructor (parentNode) {
super(parentNode);
this.header.textContent = 'Основное изображение';
this.avatar = createElement({
this.avatar = this.createElement({
tagName: 'div',
options: {
className: 'Avatar__img',
@ -28,7 +27,7 @@ class AvatarModal extends Modal {
className: 'Avatar__url-input'
});
this.submitBtn = createElement({
this.submitBtn = this.createElement({
tagName: 'button',
options: {
className: 'Avatar__submit-btn btn btn-outline-primary'
@ -53,7 +52,7 @@ class AvatarModal extends Modal {
}
init = async () => {
const user = await usersServiceApi.getSelfInfo();
const user = await profileServiceApi.getSelfInfo();
this.changeAvatar(this.url || user.avatar);
}
@ -63,7 +62,7 @@ class AvatarModal extends Modal {
submit = async () => {
if (this.url) {
await usersServiceApi.editSelfInfo({avatar: this.url});
await profileServiceApi.editSelfInfo({avatar: this.url});
userInfoService.setUserLogin();
this.hide();
} else {

View File

@ -7,5 +7,5 @@
width: 150px;
height: 150px;
cursor: pointer;
background-image: url('../../img/ava.jpg');
background-image: url('../../../../img/ava.jpg');
}

Some files were not shown because too many files have changed in this diff Show More