diff --git a/src/components/component/Component.js b/src/components/component/Component.js
index bb019ab..e59fdac 100644
--- a/src/components/component/Component.js
+++ b/src/components/component/Component.js
@@ -26,7 +26,9 @@ class Component extends EmitService {
throw new Error(message);
}
this.mainNode = content.firstElementChild.cloneNode(true);
- parentNode.appendChild(this.mainNode);
+ if (parentNode) {
+ parentNode.appendChild(this.mainNode);
+ }
this._listeners = [];
this._events = {};
}
diff --git a/src/components/logs-filters/LogsFilters.js b/src/components/logs-filters/LogsFilters.js
index 6550391..652a2eb 100644
--- a/src/components/logs-filters/LogsFilters.js
+++ b/src/components/logs-filters/LogsFilters.js
@@ -1,24 +1,93 @@
import Component from '../component/index';
import routeService from '../../services/RouteService';
+import {createElement} from '../../utils/elementUtils';
+import {LOG_TYPE, LOG_LABELS} from '../../consts';
class LogsFilters extends Component {
constructor (parentNode) {
super('#logs-filters', parentNode);
+ this.initFilter();
- this.messageInput = this.mainNode.querySelector('#logs-filter-message');
+ this.addEventListener(this.mainNode, 'submit', this.submit);
+ this.addEventListener(this.typeInput, 'change', this.changeType);
+ }
- const {message = ''} = routeService.getUrlData().query;
+ changeType = () => {
+ const tableType = this.typeInput.value;
+ routeService.pushQuery({tableType}, true);
+ const [showInputs, hideInputs] = (() => {
+ if (tableType === LOG_TYPE.SERVER) {
+ return [this.serverInputs, this.clientInputs];
+ }
+ return [this.clientInputs, this.serverInputs];
+ })();
+ hideInputs.forEach(({input, parentNode}) => {
+ input.value = '';
+ parentNode.style.display = 'none';
+ });
+ showInputs.forEach(({input, parentNode}) => {
+ input.value = '';
+ parentNode.style.display = 'block';
+ });
+ }
- this.messageInput.value = message;
+ submit = (event) => {
+ event.preventDefault();
+ const {query} = routeService.getUrlData();
- this.addEventListener(this.mainNode, 'submit', (event) => {
- event.preventDefault();
+ const inputs = query.tableType === LOG_TYPE.SERVER
+ ? this.serverInputs
+ : this.clientInputs;
- routeService.pushQuery({
- message: this.messageInput.value,
+ routeService.pushQuery(inputs.reduce((memo, {name, input}) => ({
+ ...memo,
+ [name]: input.value,
+ }), {}));
+ }
+
+ initInput = ({name, id}) => {
+ const input = this.mainNode.querySelector(`#${id}`);
+ return {
+ name,
+ input,
+ parentNode: input.parentNode,
+ };
+ }
+
+ initFilter = () => {
+ this.initTypeSelect();
+ this.serverInputs = [
+ {name: 'type', id: 'logs-filter-log-type'},
+ {name: 'message', id: 'logs-filter-message'},
+ {name: 'trace', id: 'logs-filter-trace'},
+ ].map(this.initInput);
+ this.clientInputs = [
+ {name: 'type', id: 'logs-filter-request-type'},
+ ].map(this.initInput);
+
+ this.changeType();
+ }
+
+ initTypeSelect = () => {
+ const {tableType = LOG_TYPE.SERVER} = routeService.getUrlData().query;
+ this.typeInput = this.mainNode.querySelector('#logs-filter-type');
+ LOG_LABELS.forEach(({id, label}) => {
+ createElement({
+ tagName: 'option',
+ parentNode: this.typeInput,
+ options: {
+ textContent: label,
+ },
+ args: {
+ value: id,
+ },
});
});
-
+ const {query} = routeService.getUrlData();
+ if (!query.type) {
+ routeService.pushQuery({tableType: LOG_TYPE.SERVER});
+ }
+ this.typeInput.value = tableType;
}
}
diff --git a/src/components/logs-page/LogsPage.js b/src/components/logs-page/LogsPage.js
index 611b4ba..dd2db11 100644
--- a/src/components/logs-page/LogsPage.js
+++ b/src/components/logs-page/LogsPage.js
@@ -5,14 +5,8 @@ import Pagination from '../pagination';
import LogsFilters from '../logs-filters';
import routeService from '../../services/RouteService';
import {prepareToNumber} from '../../utils/urlUtils';
-
-const COLS = [
- {id: '_id', label: 'id', width: '240px'},
- {id: 'date', label: 'Дата', width: '150px'},
- {id: 'type', label: 'Тип', width: '70px'},
- {id: 'message', label: 'Сообщение', width: '200px'},
- {id: 'trace', label: 'Стек', width: '200px'},
-];
+import {LOG_TYPE, LOG_COLS} from '../../consts';
+import {createElement, markText} from '../../utils/elementUtils';
const ELEMENTS_ON_PAGE = 15;
@@ -20,63 +14,84 @@ class LogsPage extends Component {
constructor (mainNodeSelector, parentNode) {
super(mainNodeSelector, parentNode);
- this.filters = new LogsFilters(this.mainNode);
+ this.header = createElement({
+ tagName: 'div',
+ parentNode: this.mainNode,
+ });
+ this.body = createElement({
+ tagName: 'div',
+ parentNode: this.mainNode,
+ });
+ this.footer = createElement({
+ tagName: 'div',
+ parentNode: this.mainNode,
+ });
- this.table = new Table(this.mainNode, COLS);
+ this.filters = new LogsFilters(this.header);
- this.pagination = new Pagination(this.mainNode);
+ this.tables = {
+ [LOG_TYPE.SERVER]: new Table(null, LOG_COLS[LOG_TYPE.SERVER]),
+ [LOG_TYPE.CLIENT]: new Table(null, LOG_COLS[LOG_TYPE.CLIENT]),
+ };
+
+ this.pagination = new Pagination(this.footer);
routeService.onChange(this.renderTable);
- this.pagination.onPageChange((pageNumber) => {
- const start = (pageNumber - 1) * ELEMENTS_ON_PAGE;
- const end = start + ELEMENTS_ON_PAGE;
- const rows = this.logList.slice(start, end + 1);
-
- this.table.render(rows);
- });
-
- this.loadLogList();
+ this.initPage();
}
- loadLogList = async () => {
- this.logList = await storageLogsApi.request();
+ initPage = async () => {
+ this.logList = {
+ [LOG_TYPE.SERVER]: await storageLogsApi.requestServerLogs(),
+ [LOG_TYPE.CLIENT]: await storageLogsApi.requestClientLogs(),
+ };
this.renderTable();
}
- filterRows = (queryMessage) => {
- return this.logList.reduce((memo, row) => {
- const message = row.message.toLowerCase();
- const searchMessage = (queryMessage || '').toLowerCase();
- if (searchMessage === '') {
- memo.push(row);
- return memo;
- }
- if (message.includes(searchMessage)) {
- const replaceMessage = new RegExp(searchMessage, 'g');
- const newText = message.replace(replaceMessage, `${searchMessage}`);
- memo.push({
- ...row,
- message: newText,
- });
- }
- return memo;
- }, []);
+ filterRows = (rows, query) => {
+ const filteredRows = rows.filter((row) => {
+ return Object.entries(query).every(([key, value]) => {
+ const rowValue = row[key];
+ if (!rowValue) {
+ return true;
+ }
+ return rowValue.toLowerCase().includes(value.toLowerCase());
+ });
+ });
+
+ return filteredRows.map((row) => {
+ return Object.entries(query).reduce((memo, [key, searchMessage]) => {
+ const rowValue = memo[key];
+ if (!rowValue) {
+ return memo;
+ }
+ return {
+ ...memo,
+ [key]: markText(searchMessage, rowValue),
+ };
+ }, row);
+ });
+ }
+
+ cutPagginationPage = (filteredRows, queryPageNumber) => {
+ const countPages = Math.ceil(filteredRows.length / ELEMENTS_ON_PAGE);
+ const pageNumber = prepareToNumber(queryPageNumber, countPages);
+ this.pagination.changeCountPages(countPages);
+ const start = (pageNumber - 1) * ELEMENTS_ON_PAGE;
+ const end = start + ELEMENTS_ON_PAGE;
+ return filteredRows.slice(start, end + 1);
}
renderTable = () => {
const {query} = routeService.getUrlData();
- const filterRows = this.filterRows(query.message);
- const countPages = Math.ceil(filterRows.length / ELEMENTS_ON_PAGE);
- const pageNumber = prepareToNumber(query.pageNumber, countPages);
- this.pagination.changeCountPages(countPages);
-
- const start = (pageNumber - 1) * ELEMENTS_ON_PAGE;
- const end = start + ELEMENTS_ON_PAGE;
- const rows = filterRows.slice(start, end + 1);
-
+ const {tableType, ...omitQuery} = query;
+ this.table?.mainNode?.remove();
+ this.table = this.tables[tableType];
+ this.body.appendChild(this.table.mainNode);
+ const filteredRows = this.filterRows(this.logList[tableType], omitQuery);
+ const rows = this.cutPagginationPage(filteredRows, query.pageNumber);
this.table.render(rows);
-
}
}
diff --git a/src/consts.js b/src/consts.js
new file mode 100644
index 0000000..4122460
--- /dev/null
+++ b/src/consts.js
@@ -0,0 +1,31 @@
+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: '_id', label: 'id', width: '240px'},
+ {id: 'type', label: 'Результат запроса'},
+ {id: 'request', label: 'Запрос клиента', width: '240px'},
+ {id: 'response', label: 'Ответ сервера', width: '240px'},
+ {id: 'startTime', label: 'Начало запроса', width: '150px'},
+ {id: 'endTime', label: 'Окончание запроса', width: '150px'},
+];
+
+export const LOG_COLS = {
+ [LOG_TYPE.SERVER]: SERVER_COLS,
+ [LOG_TYPE.CLIENT]: CLIENT_COLS,
+};
diff --git a/src/utils/elementUtils.js b/src/utils/elementUtils.js
index abf70a8..acfa7a4 100644
--- a/src/utils/elementUtils.js
+++ b/src/utils/elementUtils.js
@@ -32,3 +32,17 @@ export const createElement = (createElementProps) => {
parentNode.appendChild(element);
return element;
};
+
+/**
+ * Функция для поиска сообщений в тексте и их маркирование в этом тексте.
+ * @param {string} searchMessage - сообщение, которое нужно найти в тексте
+ * @param {string} text - исходный текст, в котором нужно выполнить поиск и промаркировать все сообщения
+ * @param {string} styleClassName - класс, которым нужно выделить сообщения. Выставлять не обязательно, тк есть
+ * дефолтное значение
+ */
+export const markText = (searchMessage, text, styleClassName = 'text-warning bg-dark') => {
+ const replaceMessage = new RegExp(searchMessage, 'gi');
+ return text.replace(replaceMessage, (match) => (
+ `${match}`
+ ));
+};