{#
    (C) 2020 - ntop.org

    This is the template for the active monitoring stats page. I
#}

<div class="row">
    <div class="col-md-12">
        <div class="card card-shadow">
            <div class="card-body">
                <table class="table w-100 table-striped table-hover table-bordered" id="am-table">
                    <thead>
                        <tr>
                        <th>{{i18n("flow_details.url")}}</th>
                        <th>{{i18n("system_stats.last_ip")}}</th>
			                  <th>{{i18n("active_monitoring_stats.measurement")}}</th>
                        <th>{{i18n("chart")}}</th>
                        <th>{{i18n("threshold")}}</th>
                        <th>{{i18n("active_monitoring_stats.last_24_hours")}}</th>
                        <th>{{i18n("active_monitoring_stats.last_measurement")}}</th>
                        <th>{{i18n("active_monitoring_stats.measurement")}}</th>
                        <th>{{i18n("active_monitoring_stats.alerted")}}</th>
                        <th>{{i18n("active_monitoring_stats.pool")}}</th>
                        <th>{{i18n("active_monitoring_stats.jitter")}}</th>
                        <th>{{i18n("actions")}}</th>
                        </tr>
                    </thead>
                    </table>
            </div>
            <div class="card-footer">
                {* ui_utils.render_configuration_footer('active_monitoring') *}
            </div>
        </div>
        <div class="notes bg-light border">
            <b>{{ i18n("notes") }}</b>:
            <ul>
                {% for _, note in pairs(am_stats.notes) do %}
                    <li>{{ note }}</li>
                {% end %}
            </ul>
        </div>
    </div>
    
</div>

{*
    template_utils.render("am_add_host_modal.html", {
        dialog = {
          add_record = i18n("active_monitoring_stats.add_record"),
          measurement = i18n("active_monitoring_stats.measurement"),
          add_measurement_select = generate_select("select-add-measurement", "measurement", true, false, {}, "measurement-select"),
          am_host = i18n("about.host_checks_directory"),
          periodicity = i18n("internals.periodicity"),
          add_granularity_select = generate_select("select-add-granularity", "granularity", true, false, {}, "measurement-granularity"),
          threshold = i18n("threshold"),
          notes = i18n("notes"),
          note_icmp = i18n("active_monitoring_stats.am_note_icmp"),
          note_http = i18n("active_monitoring_stats.am_note_http"),
          note_alert = i18n("active_monitoring_stats.note_alert"),
          cancel = i18n("cancel"),
          add = i18n("add"),
        }
      })
*}

{*
    template_utils.render("am_edit_host_modal.html", {
        dialog = {
          measurement = i18n("active_monitoring_stats.measurement"),
          edit_measurement_select = generate_select("select-edit-measurement", "measurement", true, false, {}, "measurement-select"),
          am_host = i18n("about.host_checks_directory"),
          periodicity = i18n("internals.periodicity"),
          edit_granularity_select = generate_select("select-edit-granularity", "granularity", true, false, {}, "measurement-granularity"),
          edit_record = i18n("active_monitoring_stats.edit_record"),
          notes = i18n("notes"),
          note_icmp = i18n("active_monitoring_stats.am_note_icmp"),
          note_http = i18n("active_monitoring_stats.am_note_http"),
          note_alert = i18n("active_monitoring_stats.note_alert"),
          note_periodicity_change = i18n("active_monitoring_stats.note_periodicity_change"),
          reset = i18n("reset"),
          apply = i18n("apply"),
          cancel = i18n("cancel"),
          threshold = i18n("threshold"),
        }
      })
*}

{*

    template_utils.render("am_delete_host_modal.html", {
        dialog = {
            confirm_delete = i18n("active_monitoring_stats.confirm_delete"),
            delete = i18n("delete"),
            cancel = i18n("cancel"),
        }
    })

*}


<script type="text/javascript">
  let get_host = "{* am_stats.get_host *}";
  let am_csrf = "{{ ntop.getRandomCSRFValue()}}";
  let import_csrf = "{{ ntop.getRandomCSRFValue()}}";
  const measurements_info = {*json.encode(am_stats.measurements_info)*};
  const poolsFilter = {* json.encode(am_stats.pool_filters)*};
  const SHOW_IFACE = {{ ntop.isPingIfaceAvailable() }};

  const MAX_RECIPIENTS = 3;
  const DEFAULT_MEASUREMENT = "cicmp";
  const INFRASTRUCTURE_ENDPOINT = "/lua/rest/v2/get/system/data.lua";

  const getMeasurementRegex = (measurement) => {
      switch (measurement) {
          default:
          case "throughput":
              return `${NtopUtils.REGEXES["url"]}|${NtopUtils.REGEXES["domainName"]}`;
          case "http":
          case "https":
              return `${NtopUtils.REGEXES["url"]}`;
          case "icmp":
          case "cicmp":
              return `${NtopUtils.REGEXES["ipv4"]}|${NtopUtils.REGEXES["ipv6"]}|${NtopUtils.REGEXES["domainName"]}`;
      }
  }

  const getMeasurementPlaceholder = (measurement) => {
      switch (measurement) {
          default:
          case "throughput":
              return `Insert an URL (https://www.google.com)`;
          case "http":
          case "https":
              return `Insert an URL (https://www.google.com)`;
          case "icmp":
          case "cicmp":
              return `Insert an IP or a Domain Name`;
      }
  }

  const addPoolFilter = (tableAPI) => {

      const POOL_COLUMN_INDEX = 8;

      return new DataTableFiltersMenu({
          filterTitle: i18n('pools.pools'),
          tableAPI: tableAPI,
          filters: poolsFilter,
          filterMenuKey: 'pools',
          columnIndex: POOL_COLUMN_INDEX
      }).init();
  }

  const addMeasurementFilter = (tableAPI) => {

      const MEASUREMENT_COLUMN_INDEX = 1;

      // build filters for datatable
      const measurements = Object.keys(measurements_info);
      const filters = [];

      for (const measurement of measurements) {
          filters.push({
              key: measurement,
              label: `${measurements_info[measurement].label}`,
              regex: `^(${measurements_info[measurement].label})$`
          });
      }

      // sort the created filters
      filters.sort((a, b) => a.label.localeCompare(b.label));

      return new DataTableFiltersMenu({
          filterTitle: i18n('active_monitoring_stats.measurement'),
          tableAPI: tableAPI,
          filters: filters,
          filterMenuKey: 'measurement',
          columnIndex: MEASUREMENT_COLUMN_INDEX
      });
  }

  const addAlertedFilter = (tableAPI) => {

      const ALERTED_COLUMN_INDEX = 8;
      const filters = [
          {
              key: 'alerted',
              label: `${i18n('active_monitoring_stats.alerted')}`,
              regex: `1`
          },
          {
              key: 'not_alerted',
              label: `${i18n('active_monitoring_stats.not_alerted')}`,
              regex: `0`
          }
      ];

      return new DataTableFiltersMenu({
          filterTitle: i18n('active_monitoring_stats.alert_status'),
          tableAPI: tableAPI,
          filters: filters,
          filterMenuKey: 'alert-status',
          columnIndex: ALERTED_COLUMN_INDEX
      });
  }

  // Disable the already defined measurements for forced_hosts since
  // they are unique
  const dialogDisableUniqueMeasurements = ($dialog, cur_measurement) => {
      const $m_sel = $dialog.find(".measurement-select");
      const measurements_to_skip = {};

      // find out wich unique measurements are already defined
      $amTable.rows().data().each(function(row_data) {
          var m_info = measurements_info[row_data.measurement];

          if(m_info && m_info.force_host)
              measurements_to_skip[row_data.measurement] = true;
      });

      // Populate the measurements dropdown
      $m_sel.find('option').remove();

// Sort measurements according to their localized labels
var sorted_measurements = [];
for(var k in measurements_info)
    sorted_measurements.push([k, measurements_info[k]]);

sorted_measurements.sort(function(a, b){
    a = a[1]; b = b[1];
    if(a.label < b.label) { return -1; }
    if(a.label > b.label) { return 1;  }
    return 0;
});

      for(var i=0; i<sorted_measurements.length; i++) {
          var k = sorted_measurements[i][0];
          var m_info = measurements_info[k];

          if((k == cur_measurement) || !measurements_to_skip[k])
              $m_sel.append(`<option value="${k}">${m_info.label}</option>`);
      }
  }

  /* Called whenever the measurment of a dialog changes/is initialized. */
  const dialogRefreshMeasurement = ($dialog, granularity, use_defaults) => {
      const measurement = $dialog.find(".measurement-select").val();

      if(!measurement || !measurements_info[measurement]) return;

      const info = measurements_info[measurement];

      $dialog.find(".measurement-operator").html("&" + (info.operator || "gt") + ";");
      $dialog.find(".measurement-unit").html(info.unit || i18n("active_monitoring_stats.msec"));

      // Check if host is forced
      const host = $dialog.find(".measurement-host")
      if(info.force_host) {
          host.attr("disabled", "disabled");
          host.val(info.force_host);
      } else {
          host.removeAttr("disabled");
      }

      // Populate the granularities dropdown
      const $granularities = $dialog.find(".measurement-granularity");
      const old_val = $granularities.val();
      let old_val_ok = false;
      $granularities.find('option').remove();

      for(let i=0; i<info.granularities.length; i++) {
          let g_info = info.granularities[i];
          if(g_info.value == old_val)
              old_val_ok = true;

          $granularities.append(`<option value="${g_info.value}">${g_info.title}</option>`);
      }

      const $threshold = $dialog.find(".measurement-threshold");

      if(info.max_threshold)
          $threshold.attr("max", info.max_threshold);
      else
          $threshold.removeAttr("max");

      if(use_defaults && info.default_threshold)
          $threshold.val(info.default_threshold);

      if(granularity)
          $granularities.val(granularity);
      else if(old_val_ok)
          $granularities.val(old_val);
  }

  const dialogRefreshPeriodicity = ($modal) => {

      const $periodicityGroup = $modal.find(`[name='granularity']`).parents('.form-group');
      const selectedMeasurement = $modal.find(".measurement-select").val();
      if (!selectedMeasurement || !measurements_info[selectedMeasurement]) return;
      const measurement = measurements_info[selectedMeasurement];

      if (measurement.granularities.length == 1) {
          $periodicityGroup.hide();
      }
      else {
          $periodicityGroup.show();
      }

  }

  const getAmData = ($am_table, $button_caller) => {

      const row_data = $am_table.row($button_caller.parent().parent()).data();
      return row_data;
  }

  const createHoursHeatmap = (td, data) => {
      const squareLength = 7, squareHeight = 20;
      const colors = ['#d3d3d3', '#28a745', '#f00', '#ffc107'];
      const $svg = $(td).find('svg');
    const this_hour = new Date().getHours();

      for (let x = 0; x < 24; x++) {
          const $rect = $(document.createElementNS("http://www.w3.org/2000/svg", "rect"));
          $rect.attr('x', x*(squareLength+2)).attr('y', 0).attr('width', squareLength).attr('height', squareHeight);
          const colorIndex = (data.length > 0) ? data[x] : 0;
          $rect.attr('fill', colors[colorIndex]);
          if (this_hour == x) {
  $rect.attr('stroke', '#000'); /* Add stroke for the current hour */
  $rect.attr('stroke-width', '2');
  $rect.attr('stroke-dasharray', '2'); /* Make line dashed */
  $rect.attr('opacity', '0.6');
    }
          $svg.append($rect);
      }
  }

  const $addHostModalHandler = $(`#am-add-form`).modalHandler({
      method: 'post',
      endpoint: `${http_prefix}/lua/edit_active_monitoring_host.lua`,
      resetAfterSubmit: false,
      csrf: am_csrf,
      onModalInit: function () {
          const $dialog = $('#am-add-modal');
          dialogDisableUniqueMeasurements($dialog);

          // select the first non-disabled option (after dialogDisableUniqueMeasurements)
          $("#select-add-measurement").val($("#select-add-measurement").find("option:not([disabled]):first").val());

          $('#input-add-host').val('');
          $('#input-add-threshold').val(100);
          $(`#am-add-modal span.invalid-feedback`).hide();
          $('#am-add-modal').modal('show');

          // set the edit pool link
          const $editPoolLink = $('#am-add-form .edit-pool');
          $editPoolLink.attr('href', NtopUtils.getEditPoolLink($editPoolLink.attr('href'), 0));

          $(`#am-add-form select[name='pool']`).trigger('change');

          dialogRefreshMeasurement($dialog, null, true /* use defaults */);
          dialogRefreshPeriodicity($dialog);
      },
      beforeSumbit: function () {
          const host = $("#input-add-host").val(), measurement = $("#select-add-measurement").val();
          const granularity = $("#select-add-granularity").val();
          const threshold = $("#input-add-threshold").val();
          const pool = $(`#select-add-pool`).val();
          const iface = $(`#am-add-form select[name='iface']`).val();

          $(`#add-invalid-feedback`).hide();

          return {
              action: 'add',
              am_host: host,
              threshold: threshold,
              measurement: measurement,
              granularity: granularity,
              pool: pool,
              ifname: iface
          }
      },
      onSubmitSuccess: function (response, dataSent) {
          if (response.success) {

              ToastUtils.showToast({
                  title: i18n("success"),
                  body: response.message,
                  level: 'success',
                  delay: 3000,
                  id: `am-add-${dataSent.am_host}`
              });

              $(`#am-add-modal`).modal('hide');
              $amTable.ajax.reload();
              return;
          }

          $(`#add-invalid-feedback`).show().text(response.error);
      }
  });

  const $editModalHandler = $(`#am-edit-form`).modalHandler({
      method: 'post',
      endpoint: `${http_prefix}/lua/edit_active_monitoring_host.lua`,
      resetAfterSubmit: false,
      csrf: am_csrf,
      onModalInit: function (amData) {
          const DEFAULT_THRESHOLD     = 500;
          const DEFAULT_GRANULARITY   = "min";
          const DEFAULT_MEASUREMENT   = "icmp";
          const DEFAULT_HOST          = "";
          const DEFAULT_POOL          = 0;

          const cur_measurement = amData.measurement_key || DEFAULT_MEASUREMENT;
          const $dialog = $('#am-edit-modal');

          $(`#hostCheck`).hide();

          dialogDisableUniqueMeasurements($dialog, cur_measurement);
          $('#am-edit-modal').modal('show');
          // fill input boxes
          $('#input-edit-threshold').val(amData.threshold || DEFAULT_THRESHOLD);
          $('#select-edit-measurement').val(cur_measurement).change();
          $('#select-edit-granularity').val(amData.granularity || DEFAULT_GRANULARITY);
          $('#input-edit-host').val(amData.host || DEFAULT_HOST);
          $(`#select-edit-pool`).val(amData.pool || DEFAULT_POOL);

          // set the edit pool link
          const $editPoolLink = $('#am-add-form .edit-pool');
          $editPoolLink.attr('href', NtopUtils.getEditPoolLink($editPoolLink.attr('href'), amData.pool || DEFAULT_POOL));
          $(`#am-edit-form select[name='pool']`).trigger('change');
          if (SHOW_IFACE && ['icmp', 'cicmp'].includes(cur_measurement)) {
  const value = (amData.ifname === '') ? $(`#am-edit-form .interface-group [name='iface'] option:first`).val() : amData.ifname;
              $(`#am-edit-form .interface-group`).show();
              $(`#am-edit-form .interface-group [name='iface']`).val(value);
    }
          else {
              $(`#am-edit-form .interface-group`).hide();
          }

          dialogRefreshMeasurement($dialog, amData.granularity);
          dialogRefreshPeriodicity($dialog);
      },
      beforeSumbit: function (amData) {
          const host = $("#input-edit-host").val();
          const measurement_key = $("#select-edit-measurement").val();
          const granularity = $("#select-edit-granularity").val();
          const threshold = $("#input-edit-threshold").val();
          const pool = $(`#select-edit-pool`).val();
          const iface = $(`#am-edit-form select[name='iface']`).val();
          $(`#am-edit-modal .invalid-feedback`).hide();

          $(`#hostCheck`).show();

          return {
              action: 'edit',
              threshold: threshold,
              am_host: host,
              measurement: measurement_key,
              old_am_host: amData.host,
              old_measurement: amData.measurement_key,
              granularity: granularity,
              old_granularity: amData.granularity,
              ifname: iface,
              pool: pool
          };
      },
      onSubmitSuccess: function (response, dataSent) {
          $(`#hostCheck`).hide();
          if (response.success) {

              ToastUtils.showToast({
                  title: i18n('success'),
                  body: response.message,
                  level: 'success',
                  delay: 3000,
                  id: `am-edit-${dataSent.am_host}`
              });

              $(`#am-edit-modal`).modal('hide');
              $amTable.ajax.reload();

              return;
          } else {
              $(`#am-edit-modal .invalid-feedback`).html(response.error).show();
          }
      }
  });

  const $removeModalHandler = $(`#am-delete-modal form`).modalHandler({
      method: 'post',
      csrf: am_csrf,
      endpoint: `${http_prefix}/lua/edit_active_monitoring_host.lua`,
      dontDisableSubmit: true,
      onModalInit: function(amData) {
          $("#delete-host").html(`<b>${amData.measurement}://${amData.html_label}</b>`);
      },
      beforeSumbit: (amData) => {
          return {
              action: 'delete',
              am_host: amData.host,
              measurement: amData.measurement_key,
              csrf: am_csrf
          }
      },
      onSubmitSuccess: function (response, dataSent) {
          if (response.success) {
              $(`#am-delete-modal`).modal('hide');
              ToastUtils.showToast({
                  title: i18n('success'),
                  body: response.message,
                  level: 'success',
                  delay: 3000,
                  id: `am-delete-${dataSent.am_host}`
              });
              $amTable.ajax.reload();
          }
      }
  });

  let dtConfig = DataTableUtils.getStdDatatableConfig( [
      {
          text: '<i class="fas fa-plus"></i>',
          className: 'btn-link',
          enabled: (get_host === ""),
          action: function(e, dt, node, config) {
              $addHostModalHandler.invokeModalInit();
              $("#select-add-measurement").trigger('change');
          }
      },
      {
          text: '<i class="fas fa-sync-alt"></i>',
          className: 'btn-link',
          action: function(e, dt, node, config) {
              $amTable.ajax.reload();
          }
      }
  ], );
  dtConfig = DataTableUtils.setAjaxConfig(dtConfig, `${http_prefix}/lua/get_active_monitoring_hosts.lua`);
  dtConfig = DataTableUtils.extendConfig(dtConfig, {
      lengthChange: (get_host === ""),
      paging: (get_host === ""),
      order: [[ 1 /* Last IP */, "desc" ]],
      initComplete: function(settings, data) {

          if (get_host != "") {
              $amTable.search(get_host).draw(true);
              $amTable.state.clear();
          }

          const table = settings.oInstance.api();
          setInterval(() => { $amTable.ajax.reload(); }, 15000);
      },
      columns: [
          {
              data: 'html_label',
              width: '100%',
          render: function(html_label, type, row) {
                  
                  if (type === 'display') {
                      if (html_label == "" || html_label == undefined) return "";

                    if(row.alerted) {
                        return `${html_label} <i class="fas fa-exclamation-triangle" style="color: #f0ad4e;"></i>`
                          }
                          
                          return html_label;
                  }

                  return (row.label);
              }
          },
          {
              data: 'last_ip',
              className: 'dt-body-right dt-head-center text-nowrap'
          },
          {
              data: 'measurement',
              class: 'text-nowrap',
          },
          {
              data: 'chart',
              class: 'text-center',
              sortable: false,
                render: function(href, type, row) {
                  if(type === 'display') {
                      if (href == "" || href == undefined) return "";
                      return `<a href='${href}'><i class='fas fa-chart-area'></i></a>`
                  }

                  // The raw data must be returned here for sorting
                  return(href);
              }
          },
          {
              data: 'threshold',
              className: 'text-center',
              render: function(data, type, row) {
                  if(type === 'display' || type === 'filter') {
                      if(row.threshold)
                          return `${row.threshold} ${row.unit}`
                      else
                          return "";
                  }

                  // The raw data must be returned here for sorting
                  return(data);
              }
          },
          {
              data: 'hours',
              className: 'text-center dt-head-center text-nowrap',
              sortable: false,
              render: function(data, type) {
                  if (type == 'display') {
                      return `<svg width='220' height='20' viewBox='0 0 220 20'></svg>`;
                  }
                  return data;
              },
              createdCell: function(td, data) {
                  createHoursHeatmap(td, data);
              }
          },
          {
              data: 'last_mesurement_time',
              className: 'text-center text-nowrap',
  render: $.fn.dataTableExt.absoluteFormatSecondsToHHMMSS,
          },
          {
              data: 'last_measure',
              className: 'text-center',
              sortable: false,
              render: function(data, type, row) {
                  if(type === 'display' || type === 'filter') {
                      if (row.last_measure) {
                        return (parseFloat(row.last_measure) != NaN ? `${parseFloat(row.last_measure)} ${row.unit}` : `${row.last_measure}`)
                      }
                      else
                          return "";
                  }

                  // The raw data must be returned here for sorting
                  return(data);
              }
          },
          {
              data: 'alerted',
              visible: false,
              sortable: false,
          },
          {
              data: 'pool',
              visible: false,
              sortable: false
          },
          {
              data: 'jitter',
              className: 'dt-body-right dt-head-center no-wrap',
              sortable: false,
          },
          {
              targets: -1,
              data: null,
              sortable: false,
              name: 'actions',
              responsivePriority: 3,
              class: 'text-center',
              render: function(_, type, host) {

                  const disabled = (host.readonly) ? 'disabled' : '';

                  return DataTableUtils.createActionButtons([
                      { class: `btn-info ${disabled}`, icon: 'fa-edit', modal: '#am-edit-modal', title: `${i18n("users.edit")}` },
                      { class: `btn-danger ${disabled}`, icon: 'fa-trash', modal: '#am-delete-modal', title: `${i18n("delete")}` }
                  ]);
              }
          },
    {
              data: 'measurement_key',
  visible: false
          }
      ]
  });

  const $amTable = $("#am-table").DataTable(dtConfig);
  DataTableUtils.addToggleColumnsDropdown($amTable);

  addMeasurementFilter($amTable).init();
  addAlertedFilter($amTable).init();

  $('#am-table').on('click', `a[href='#am-edit-modal']`, function(e) {
      const amData = getAmData($amTable, $(this).parent().parent());
      $editModalHandler.invokeModalInit(amData);
  });

  $('#am-table').on('click', `a[href='#am-delete-modal']`, function(e) {
      const amData = getAmData($amTable, $(this).parent().parent());
      $removeModalHandler.invokeModalInit(amData);
  });

  $("#select-edit-measurement").on('change', function(event) {
      const selectedMeasurement = $(this).val();
      // change the pattern depending on the selected measurement
      $(`#input-edit-host`).attr('pattern', getMeasurementRegex(selectedMeasurement));

      // trigger form validation
      if ($(`#input-edit-host`).val().length > 0) $(`#am-edit-form`)[0].reportValidity();
      if (['icmp', 'cicmp'].includes(selectedMeasurement)) {
          if (SHOW_IFACE) {
          $(`#am-edit-form .interface-group`).show();
          }
      }
      else {
          $(`#am-edit-form .interface-group`).hide();
      }

      dialogRefreshMeasurement($("#am-edit-modal"));
      dialogRefreshPeriodicity($("#am-edit-modal"));
  });

  // select the first pattern based to the first selected measurement
  // on the input-add-host
  $(`#input-add-host`).attr('pattern', getMeasurementRegex($("#select-add-measurement").val()));

  $("#select-add-measurement").on('change', function(event) {

      const selectedMeasurement = $(this).val();

      // change the pattern depending on the selected measurement
      $(`#input-add-host`).attr('pattern', getMeasurementRegex(selectedMeasurement));
      $(`#input-add-host`).attr('placeholder',getMeasurementPlaceholder(selectedMeasurement));

      // trigger form validation
      if ($(`#input-add-host`).val().length > 0) $(`#am-add-form`)[0].reportValidity();

      if (['icmp', 'cicmp'].includes(selectedMeasurement)) {
    if (SHOW_IFACE) {
  $(`#am-add-form .interface-group`).show();
    }
      }
      else {
          $(`#am-add-form .interface-group`).hide();
      }

      dialogRefreshMeasurement($("#am-add-modal"));
      dialogRefreshPeriodicity($("#am-add-modal"));
  });

  // on changing the associated pool updates the link to the edit pool
  $(`select[name='pool']`).change(async function() {

      const poolId = $(this).val();
      const $editPoolLink = $(this).parents('.form-group').find('.edit-pool');
      const $recipientsInfo = $(this).parents('.form-group').find('.recipients-info')

      $editPoolLink.attr('href', NtopUtils.getEditPoolLink($editPoolLink.attr('href'), poolId));

      const [success, pool] = await NtopUtils.getPool('active_monitoring', poolId);
      if (!success) return;

      let recipients = pool.recipients;

      if (recipients.length == 0) {
          $recipientsInfo.html(i18n('pools.no_recipients'));
          return;
      }

      const recipientNames = NtopUtils.arrayToListString(recipients.map(recipient => recipient.recipient_name), MAX_RECIPIENTS);
      $recipientsInfo.html(i18n('pools.some_recipients').replace('${recipients}', recipientNames));

  });
</script>

