Source: engine/universal/ui/src/display-items/logging/index.js

/* global window document console PROCEED_DATA */
import '../pure-base-min.css';
import '../style-buttons.css';
import '../style-forms.css';
import '../style-colors.css';
import './style.css';

import { appendElem } from '../helpers.js';

let doRefresh = true;
function toggleRefresh() {
  document.getElementById('toggleBtn').innerHTML = mapVal(!doRefresh ? 'pause' : 'play');
  doRefresh = !doRefresh;
}

window.toggleRefresh = toggleRefresh;

let charMap = {
  info: '➤',
  debug: '🛠',
  error: '⚠',
  trace: '🛡',

  CORE: '◉',
  SYSTEM: '⚙',

  pause: '⏸',
  play: '▶',
};

function mapVal(val) {
  let mapped = charMap[val];
  if (mapped) return mapped;
  return val;
}
function optMapVal(val) {
  let mapedVal = mapVal(val);
  if (mapedVal === val) {
    return '';
  } else {
    return mapedVal;
  }
}

const LogRoot = document.getElementById('LogRoot');

const tableSelection = document.getElementById('table-select');
tableSelection.addEventListener('change', ({ target }) => {
  selectedInstance = 'No instance';
  setSelectedTable(target[target.selectedIndex].value);
  renderData(currentData);
});

const instanceSelection = document.getElementById('instance-select');
instanceSelection.addEventListener('change', ({ target }) => {
  selectedInstance = target[target.selectedIndex].value;
  renderData(currentData);
});

function refreshData() {
  if (LogRoot && doRefresh) {
    window.PROCEED_DATA.get('/logging/api/log').then((e) => {
      renderData(e);
    });
  }
}
refreshData();
window.setInterval(refreshData, 2000);

let currentData;
let selectedTable = 'standard';

function setSelectedTable(tableName) {
  selectedTable = tableName;

  if (tableName === 'standard') {
    instanceSelection.style.display = '';
  } else {
    instanceSelection.style.display = 'block';
  }
}

/**
 * Checks if there are new tables or tables were removed
 *
 * @param {Object} data new logging data
 * @returns {Boolean} if the list of tables changed
 */
function tablesChanged(data) {
  if (!currentData) {
    return true;
  }

  const currentTables = Object.keys(currentData);
  const newTables = Object.keys(data);

  // early return if number of tables changed
  if (currentTables.length !== newTables.length) {
    return true;
  }

  // see if both lists contain the same elements
  return currentTables.some((table) => !newTables.includes(table));
}

function renderData(data) {
  // rerender table selection if some table was removed or added
  // TODO: only add new entries
  if (tablesChanged(data)) {
    renderSelection(Object.keys(data), tableSelection, selectedTable);
  }

  currentData = data;

  LogRoot.innerHTML = '';

  if (!data[selectedTable]) {
    setSelectedTable('standard');
  }
  if (selectedTable === 'standard') {
    renderStandardLogs(data['standard']);
  } else {
    renderProcessLogs(data[selectedTable]);
  }
}

function renderStandardLogs(logs) {
  logs.forEach((log) => {
    const [entry] = Object.values(log);
    LogRoot.appendChild(createLogElem(entry));
  });
}

let selectedInstance;

function renderProcessLogs(logs) {
  const instances = {};

  // sort logs by instance
  logs.forEach((log) => {
    [log] = Object.values(log);

    if (!instances[log.instanceId]) {
      instances[log.instanceId] = [];
    }

    instances[log.instanceId].push(log);
  });

  renderSelection(['No instance', ...Object.keys(instances)], instanceSelection, selectedInstance);

  // render log entries for instances
  Object.values(instances).forEach((instance) => {
    if (
      selectedInstance &&
      selectedInstance !== 'No instance' &&
      selectedInstance !== instance[0].instanceId
    ) {
      return;
    }

    const instanceContainer = appendElem(LogRoot, 'div', {
      class: 'container instance-container',
    });
    const title = appendElem(instanceContainer, 'div', {
      class: 'title-container',
    });
    title.textContent = instance[0].instanceId;

    instance.forEach((log) => {
      instanceContainer.appendChild(createLogElem(log));
    });
  });
}

function renderSelection(data, selectElement, currentValue) {
  selectElement.innerHTML = '';

  data.forEach((entry) => {
    const option = appendElem(selectElement, 'option', {
      value: entry,
    });

    if (entry === currentValue) {
      option.setAttribute('selected', true);
    }

    option.textContent = entry;
  });
}

function createLogElem(logElement) {
  let { msg, level, time, moduleName } = logElement;
  time = new Intl.DateTimeFormat('en-GB', {
    hour: 'numeric',
    minute: 'numeric',
    second: 'numeric',
    year: 'numeric',
    month: 'numeric',
    day: 'numeric',
  }).format(new Date(time));

  let LogElem = appendElem(null, 'div', { class: `LogElem log-${level}` });

  appendElem(LogElem, 'div', {
    class: 'data level icon',
  }).textContent = optMapVal(level);
  appendElem(LogElem, 'div', { class: 'data level time' }).textContent = time;
  appendElem(LogElem, 'div', { class: 'data level text' }).textContent = `${level.toUpperCase()}: `;

  appendElem(LogElem, 'div', {
    class: 'data moduleName text',
  }).textContent = moduleName;
  appendElem(LogElem, 'div', { class: 'data msg' }).textContent = msg;

  return LogElem;
}