PageSourceSearch

https://webhost1.ru/assets/1966.6ac9f24fd1b2ceac03d6.js

js webhost1.ru collected 2026-10-02 04:50:29 UTC 70,036 bytes, 3 lines download raw bytes

1/*! For license information please see 1966.6ac9f24fd1b2ceac03d6.js.LICENSE.txt */
2(self.webpackChunkwebhost1=self.webpackChunkwebhost1||[]).push([[1966],{50382:(e,n,r)=>{r.r(n),r.d(n,{default:()=>i});var a=r(71354),s=r.n(a),o=r(76314),l=r.n(o)()(s());l.push([e.id,"\n.support-heatmap th[data-v-22404036],\n.support-heatmap td[data-v-22404036] {\n  padding: 4px 6px;\n  font-size: 12px;\n  min-width: 44px;\n  white-space: nowrap;\n}\n.support-heatmap .cell-triple[data-v-22404036] {\n  display: inline-block;\n  line-height: 1.1;\n  font-weight: 700;\n  text-shadow: 0 0 2px rgba(255, 255, 255, 0.85);\n}\n/* Насыщенные цвета — для контраста на зелёном фоне градиента (фон сам по\n   себе светлый, alpha ≤ 0.35, см. cellStyle), плюс белый text-shadow. */\n.support-heatmap .cell-a[data-v-22404036] { color: #1a5c34;\n}   /* зелёный — ответов */\n.support-heatmap .cell-b[data-v-22404036] { color: #004e96;\n}   /* синий — тредов */\n.support-heatmap .cell-c[data-v-22404036] { color: #b35e00;\n}   /* оранжевый — закрыто */\n.support-heatmap .sep[data-v-22404036]    { color: #888; margin: 0 1px; font-weight: 400;\n}\n.support-heatmap .month-row td[data-v-22404036] {\n  background: #eaf2ec;\n  color: #1a5c34;\n  font-size: 13px;\n  padding: 4px 8px !important;\n  border-top: 1px solid #cfdfd4;\n  border-bottom: 1px solid #cfdfd4;\n}\n.support-heatmap .month-row td b[data-v-22404036] { font-size: 14px;\n}\n.support-heatmap .month-row td small[data-v-22404036] { color: #6b8b76 !important;\n}\n.chart-wrap[data-v-22404036] {\n  max-width: 100%;\n  background: #fff;\n  padding: 12px;\n  border: 1px solid #dee2e6;\n  border-radius: 4px;\n}\n.support-heatmap td[data-v-22404036]:first-child {\n  white-space: nowrap;\n}\n.metrics-legend[data-v-22404036] {\n  background: #f5f7f5;\n  border-left: 3px solid #2d7a4a;\n  padding: 10px 14px;\n  font-size: 13px;\n}\n.metrics-legend ul[data-v-22404036] {\n  padding-left: 18px;\n  margin-top: 6px;\n}\n.metrics-legend li[data-v-22404036] {\n  margin-bottom: 4px;\n}\n.metrics-legend code[data-v-22404036] {\n  background: #e9ecef;\n  padding: 0 4px;\n  border-radius: 3px;\n  font-size: 12px;\n}\n.support-stats-report .loading-state[data-v-22404036] {\n  background: #f8f9fa;\n  border: 1px solid #e9ecef;\n  border-radius: 4px;\n}\n/* Отступ между группой «Быстрые периоды» и «Обновить/Очистить кеш» в одной\n   flex-строке (без визуального разделителя). */\n.support-stats-report .filter-divider[data-v-22404036] {\n  display: inline-block;\n  width: 16px;\n}\n.support-stats-report th.sortable[data-v-22404036] {\n  cursor: pointer;\n  user-select: none;\n}\n.support-stats-report th.sortable[data-v-22404036]:hover {\n  background: #f5f5f5;\n}\n.support-stats-report .sort-ind[data-v-22404036] {\n  color: #2d7a4a;\n  font-weight: 700;\n  margin-left: 2px;\n}\n/* Верхний scrollbar над heatmap. Высота ~15px — стандартная толщина системного\n   скроллбара. Внутри пустой `<div>` ровно той же ширины что и таблица — это\n   создаёт горизонтальный overflow и заставляет браузер отрисовать scrollbar. */\n.support-stats-report .heatmap-top-scroll[data-v-22404036] {\n  overflow-x: auto;\n  overflow-y: hidden;\n  margin-bottom: 4px;\n  height: 15px;\n}\n","",{version:3,sources:["webpack://./webpack/common/vue/component/admin/reports/SupportStatsReport.vue"],names:[],mappings:";AAosBA;;EAEE,gBAAgB;EAChB,eAAe;EACf,eAAe;EACf,mBAAmB;AACrB;AACA;EACE,qBAAqB;EACrB,gBAAgB;EAChB,gBAAgB;EAChB,8CAA8C;AAChD;AACA;wEACwE;AACxE,4CAA2B,cAAc;AAAE,IAAI,sBAAsB;AACrE,4CAA2B,cAAc;AAAE,IAAI,mBAAmB;AAClE,4CAA2B,cAAc;AAAE,IAAI,wBAAwB;AACvE,4CAA2B,WAAW,EAAE,aAAa,EAAE,gBAAgB;AAAE;AACzE;EACE,mBAAmB;EACnB,cAAc;EACd,eAAe;EACf,2BAA2B;EAC3B,6BAA6B;EAC7B,gCAAgC;AAClC;AACA,oDAAmC,eAAe;AAAE;AACpD,wDAAuC,yBAAyB;AAAE;AAClE;EACE,eAAe;EACf,gBAAgB;EAChB,aAAa;EACb,yBAAyB;EACzB,kBAAkB;AACpB;AACA;EACE,mBAAmB;AACrB;AACA;EACE,mBAAmB;EACnB,8BAA8B;EAC9B,kBAAkB;EAClB,eAAe;AACjB;AACA;EACE,kBAAkB;EAClB,eAAe;AACjB;AACA;EACE,kBAAkB;AACpB;AACA;EACE,mBAAmB;EACnB,cAAc;EACd,kBAAkB;EAClB,eAAe;AACjB;AACA;EACE,mBAAmB;EACnB,yBAAyB;EACzB,kBAAkB;AACpB;AACA;+CAC+C;AAC/C;EACE,qBAAqB;EACrB,WAAW;AACb;
2AACA;EACE,eAAe;EACf,iBAAiB;AACnB;AACA;EACE,mBAAmB;AACrB;AACA;EACE,cAAc;EACd,gBAAgB;EAChB,gBAAgB;AAClB;AACA;;+EAE+E;AAC/E;EACE,gBAAgB;EAChB,kBAAkB;EAClB,kBAAkB;EAClB,YAAY;AACd",sourcesContent:['<template>\n  <div class="support-stats-report">\n    <h3 class="mb-3">{{ t(\'title\') }}</h3>\n\n    <div class="filter-section mb-4">\n      <div class="row">\n        <div class="col-md-2 col-sm-6 mb-3">\n          <label class="form-label">{{ t(\'filters.dateFrom\') }}</label>\n          <input type="date" v-model="filters.from" class="form-control" @change="loadData" />\n        </div>\n        <div class="col-md-2 col-sm-6 mb-3">\n          <label class="form-label">{{ t(\'filters.dateTo\') }}</label>\n          <input type="date" v-model="filters.to" class="form-control" @change="loadData" />\n        </div>\n        <div class="col-md-3 col-sm-6 mb-3">\n          <label class="form-label">{{ t(\'filters.support\') }}</label>\n          <select v-model.number="filters.support" class="form-control" @change="loadData">\n            <option :value="-1">{{ t(\'filters.allSupports\') }}</option>\n            <option v-for="s in allSupports" :key="s.id" :value="s.id">\n              #{{ s.id }} — {{ s.name }}\n            </option>\n          </select>\n        </div>\n        <div class="col-md-5 col-sm-12 mb-3">\n          <label class="form-label">{{ t(\'filters.quickPeriods\') }}</label>\n          <div class="d-flex flex-wrap align-items-center">\n            <button type="button" class="btn btn-outline-primary me-2 mb-2" @click="setQuick(\'today\')">\n              {{ t(\'filters.today\') }}\n            </button>\n            <button type="button" class="btn btn-outline-primary me-2 mb-2" @click="setQuick(\'week\')">\n              {{ t(\'filters.week\') }}\n            </button>\n            <button type="button" class="btn btn-outline-primary me-2 mb-2" @click="setQuick(\'month\')">\n              {{ t(\'filters.month\') }}\n            </button>\n            <button type="button" class="btn btn-outline-primary me-2 mb-2" @click="setQuick(\'quarter\')">\n              {{ t(\'filters.quarter\') }}\n            </button>\n            <button type="button" class="btn btn-outline-primary me-2 mb-2" @click="setQuick(\'year\')">\n              {{ t(\'filters.year\') }}\n            </button>\n            <span class="filter-divider mb-2" aria-hidden="true" />\n            <button\n              type="button"\n              class="btn btn-outline-secondary me-2 mb-2"\n              :disabled="loading"\n              @click="loadData"\n            >\n              <span v-if="loading">{{ t(\'filters.loading\') }}</span>\n              <span v-else>{{ t(\'filters.refresh\') }}</span>\n            </button>\n            <button\n              type="button"\n              class="btn btn-outline-warning me-2 mb-2"\n              :disabled="loading"\n              :title="t(\'filters.clearCacheTitle\')"\n              @click="clearCache"\n            >\n              {{ t(\'filters.clearCache\') }}\n            </button>\n          </div>\n        </div>\n      </div>\n      <div class="row">\n        <div class="col-md-12 mb-2">\n          <label class="form-label fw-bold me-3">{{ t(\'filters.kind\') }}</label>\n          <div class="btn-group" role="group">\n            <button\n              type="button"\n              class="btn btn-sm"\n              v-for="k in kindOptions"\n              :key="k.code"\n              :class="filters.kind === k.code ? \'btn-primary\' : \'btn-outline-primary\'"\n              @click="filters.kind = k.code; loadData()"\n            >\n              {{ t(k.labelKey) }}\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <div v-if="loading" class="loading-state d-flex align-items-center justify-content-center py-5">\n      <div class="spinner-border text-primary me-3" role="status" aria-hidden="true" />\n      <div>\n        <div class="fw-bold">{{ t(\'filters.loading\') }}</div>\n        <div class="text-muted small">{{ t(\'loadingHint\') }}</div>\n      </div>\n    </div>\n\n    <div v-if="error" class="alert alert-danger">{{ error }}</div>\n\n    <div v-if="period && period.cached && !loading" class="text-muted small mb-2">\n      <i class="fa fa-database me-1" /> {{ t(\'fromCache\') }}\n    </div>\n\n    <h4 class="mt-4 mb-2">{{ t(\'rating\') }}</h4>\n\n    <div class="metrics-legend mb-3">\n      <div><b>{{ t(\'legend.title\') }}</b></div>\n      <ul class="mb-0">\n        <li v-html="t(\'legend.answers\')" />\n        <li v-html="t(\'legend.threads\')" />\n        <li v-html="t(\'legend.closed\')" />\n        <li v-html="t(\'legend.thanksDislikes\')" />\n        <li v-html="t(\'legend.ratedPct\')" />\n        <li v-html="t(\'legend.rateFromAnswers\')" />\n        <li v-html="t(\'legend.rateFromRated\')" />\n        <li v-html="t(\'legend.rateNet\')" />\n      </ul>\n    </div>\n\n    <div class="table-responsive mb-4">\n      <table class="table table-bordered table-striped table-sm">\n        <thead>\n          <tr>\n            <th>#</th>\n            <th class="sortable" @click="toggleSort(\'support_name\')">\n              {{ t(\'table.support\') }}<span class="sort-ind">{{ sortInd(\'support_name\') }}</span>\n            </th>\n            <th class="text-right sortable" @click="toggleSort(\'answers\')">\n              {{ t(\'table.answers\') }}<span class="sort-ind">{{ sortInd(\'answers\') }}</span>\n            </th>\n            <th class="text-right sortable" @click="toggleSort(\'threads\')">\n              {{ t(\'table.threads\') }}<span class="sort-ind">{{ sortInd(\'threads\') }}</span>\n            </th>\n            <th class="text-right sortable" @click="toggleSort(\'closed\')">\n              {{ t(\'table.closed\') }}<span class="sort-ind">{{ sortInd(\'closed\') }}</span>\n            </th>\n            <th class="text-right sortable" @click="toggleSort(\'thanks_count\')">\n              {{ t(\'table.thanks\') }}<span class="sort-ind">{{ sortInd(\'thanks_count\') }}</span>\n            </th>\n            <th class="text-right sortable" @click="toggleSort(\'dislike_count\')">\n              {{ t(\'table.dislikes\') }}<span class="sort-ind">{{ sortInd(\'dislike_count\') }}</span>\n            </th>\n            <th class="text-right sortable" :title="t(\'table.tooltipRatedPct\')" @click="toggleSort(\'rated_pct\')">\n              {{ t(\'table.ratedPct\') }}<span class="sort-ind">{{ sortInd(\'rated_pct\') }}</span>\n            </th>\n            <th class="text-right sortable" :title="t(\'table.tooltipRateFromAnswers\')" @click="toggleSort(\'rate_from_answers\')">\n              {{ t(\'table.rateFromAnswers\') }}<span class="sort-ind">{{ sortInd(\'rate_from_answers\') }}</span>\n            </th>\n            <th class="text-right sortable" :title="t(\'table.tooltipRateFromRated\')" @click="toggleSort(\'rate_from_rated\')">\n              {{ t(\'table.rateFromRated\') }}<span class="sort-ind">{{ sortInd(\'rate_from_rated\') }}</span>\n            </th>\n            <th class="text-right sortable" :title="t(\'table.tooltipRateNet\')" @click="toggleSort(\'rate_net\')">\n              {{ t(\'table.rateNet\') }}<span class="sort-ind">{{ sortInd(\'rate_net\') }}</span>\n            </th>\n          </tr>\n        </thead>\n        <tbody>\n          <tr v-for="(row, i) in sortedSummary" :key="row.support_id">\n            <td>{{ i + 1 }}</td>\n            <td>\n              #{{ row.support_id }}
2 — {{ row.support_name }}\n            </td>\n            <td class="text-right"><b>{{ row.answers }}</b></td>\n            <td class="text-right">{{ row.threads }}</td>\n            <td class="text-right">{{ row.closed }}</td>\n            <td class="text-right text-success">{{ row.thanks_count }}</td>\n            <td class="text-right text-danger">{{ row.dislike_count }}</td>\n            <td class="text-right">\n              <span v-if="row.rated_pct !== null">{{ row.rated_pct }}%</span>\n              <span v-else class="text-muted">—</span>\n            </td>\n            <td class="text-right">\n              <span v-if="row.rate_from_answers !== null">{{ row.rate_from_answers }}%</span>\n              <span v-else class="text-muted">—</span>\n            </td>\n            <td class="text-right">\n              <span v-if="row.rate_from_rated !== null">{{ row.rate_from_rated }}%</span>\n              <span v-else class="text-muted">—</span>\n            </td>\n            <td class="text-right">\n              <span v-if="row.rate_net !== null" :class="rateNetClass(row.rate_net)">\n                {{ row.rate_net > 0 ? \'+\' : \'\' }}{{ row.rate_net }}%\n              </span>\n              <span v-else class="text-muted">—</span>\n            </td>\n          </tr>\n          <tr v-if="summary.length === 0">\n            <td colspan="11" class="text-center text-muted">{{ t(\'table.noData\') }}</td>\n          </tr>\n        </tbody>\n      </table>\n    </div>\n\n    <h4 class="mt-4 mb-2 d-flex align-items-center flex-wrap">
2\n      <span>{{ t(\'details.title\') }}</span>\n      <div class="btn-group ms-3" role="group">\n        <button\n          type="button"\n          class="btn btn-sm"\n          :class="view === \'table\' ? \'btn-primary\' : \'btn-outline-primary\'"\n          @click="view = \'table\'"\n        >\n          {{ t(\'details.table\') }}\n        </button>\n        <button\n          type="button"\n          class="btn btn-sm"\n          :class="view === \'chart\' ? \'btn-primary\' : \'btn-outline-primary\'"\n          @click="view = \'chart\'"\n        >\n          {{ t(\'details.chart\') }}\n        </button>\n      </div>\n    </h4>\n    <p class="text-muted small mb-2">\n      <span v-html="t(\'details.hint\')" />\n      <br>\n      <span v-html="t(\'details.hintNote\')" />\n    </p>\n\n    \x3c!-- Дублирующая верхняя горизонтальная полоса прокрутки. Высота 1px-stub\n         внутри даёт нужную ширину, но визуально пустая строка. scroll-event\n         двусторонне синхронизирован с основной таблицей ниже. --\x3e\n    <div\n      v-if="view === \'table\'"\n      ref="heatmapTopScroll"\n      class="heatmap-top-scroll"\n      @scroll="syncHeatmapScroll(\'top\')"\n    >\n      <div :style="{ width: heatmapWidth + \'px\', height: \'1px\' }" />\n    </div>\n    <div\n      v-if="view === \'table\'"\n      ref="heatmapTableScroll"\n      class="table-responsive"\n      @scroll="syncHeatmapScroll(\'bottom\')"\n    >\n      <table ref="heatmapTable" class="table table-bordered table-sm support-heatmap">\n        <thead>\n          <tr>\n            <th>{{ t(\'heatmap.day\') }}</th>\n            <th v-for="h in 24" :key="h" class="text-center">{{ h - 1 }}</th>\n            <th class="text-center">{{ t(\'heatmap.sumShort\') }}</th>\n          </tr>\n        </thead>\n        <tbody>\n          <template v-for="row in heatmapRows" :key="row.key">\n            <tr v-if="row.type === \'month\'" class="month-row">\n              <td colspan="26">\n                <b>{{ row.label }}</b>\n                <small class="text-muted ms-2">{{ row.daysCount }} {{ pluralDays(row.daysCount) }}</small>\n              </td>\n            </tr>\n            <tr v-else>\n              <td><small>{{ row.day }}</small></td>\n              <td\n                v-for="h in 24"\n                :key="h"\n                class="text-center"\n                :style="cellStyle(cellAt(row.day, h - 1).answers)"\n                :title="cellTitle(row.day, h - 1)"\n              >\n                <template v-if="cellAt(row.day, h - 1).answers || cellAt(row.day, h - 1).threads || cellAt(row.day, h - 1).closed">\n                  <span class="cell-triple">\n                    <b class="cell-a">{{ cellAt(row.day, h - 1).answers }}</b>\n                    <span class="sep">/</span>\n                    <b class="cell-b">{{ cellAt(row.day, h - 1).threads }}</b>\n                    <span class="sep">/</span>\n                    <b class="cell-c">{{ cellAt(row.day, h - 1).closed }}</b>\n                  </span>\n                </template>\n                <span v-else class="text-muted">·</span>\n              </td>\n              <td class="text-center">\n                <span class="cell-triple">\n                  <b class="cell-a">{{ daySum(row.day).answers }}</b>\n                  <span class="sep">/</span>\n                  <b class="cell-b">{{ daySum(row.day).threads }}</b>\n                  <span class="sep">/</span>\n                  <b class="cell-c">{{ daySum(row.day).closed }}</b>\n                </span>\n              </td>\n            </tr>\n          </template>\n          <tr v-if="heatmapDays.length === 0">\n            <td colspan="26" class="text-center text-muted">{{ t(\'heatmap.noData\') }}</td>\n          </tr>\n        </tbody>\n      </table>\n    </div>\n\n    <div v-else class="chart-wrap">\n      <canvas ref="chartCanvas" />\n    </div>\n  </div>\n</template>\n\n<script>\nimport { defineComponent } from \'vue\'\nimport axios from \'axios\'\nimport { Chart, registerables } from \'chart.js\'\nimport translate from \'../../../functions/translate\'\n\nChart.register(...registerables)\n\nconst TRANSLATION_ROOT = \'message.admin.reports.supportStats.\'\n\nconst todayIso = () => new Date().toISOString().slice(0, 10)\nconst monthStartIso = () => {\n  const d = new Date()\n  d.setDate(1)\n  return d.toISOString().slice(0, 10)\n}\n\nexport default defineComponent({\n  name: \'SupportStatsReport\',\n  data() {\
2n    return {\n      filters: {\n        from: monthStartIso(),\n        to: todayIso(),\n        support: -1,\n        kind: \'all\',\n      },\n      kindOptions: [\n        { code: \'all\',    labelKey: \'filters.kindAll\' },\n        { code: \'ticket\', labelKey: \'filters.kindTickets\' },\n        { code: \'chat\',   labelKey: \'filters.kindChats\' },\n      ],\n      summary: [],\n      hourly: [],          // A + B по часу (tickets.date)\n      hourlyClosed: [],    // C по часу (tickets.answered)\n      dailyThreads: {},    // {support_id: {day: threads}} — корректный COUNT(DISTINCT parent) по дню\n      allSupports: [],     // [{id, name}] — полный список для <select>, не зависит от фильтра\n      period: null,        // meta из API: {from, to, from_iso, to_iso, cached}\n      loading: false,\n      sortBy: \'answers\',   // колонка сортировки рейтинга (по умолчанию — Ответов)\n      sortDir: \'desc\',\n      heatmapWidth: 0,     // ширина внутренней таблицы heatmap — для верхнего scroll-stub\'а\n      _syncing: false,     // флаг, чтобы scroll-эхо не зацикливалось\n      error: null,\n      view: \'table\',\n      chart: null,\n    }\n  },\n  computed: {\n    sortedSummary() {\n      const field = this.sortBy\n      const dir = this.sortDir === \'asc\' ? 1 : -1\n      return [...this.summary].sort((a, b) => {\n        const av = a[field]\n        const bv = b[field]\n        // null всегда внизу при любом направлении — иначе колонки с null\n        // (нет оценок) забивают верх таблицы.\n        if (av === null && bv === null) return 0\n        if (av === null) return 1\n        if (bv === null) return -1\n        if (typeof av === \'string\') return av.localeCompare(bv) * dir\n        return (av - bv) * dir\n      })\n    },\n    /**\n     * heatmap[day][hour] = {answers, threads, closed} — все три метрики\n     * сразу в одной ячейке. Свёртка по саппортам (если их > 1) — суммы.\n     */\n    heatmap() {\n      const map = {}\n      const ensure = (day, hour) => {\n        if (!map[day]) map[day] = {}\n        if (!map[day][hour]) map[day][hour] = { answers: 0, threads: 0, closed: 0 }\n        return map[day][hour]\n      }\n      this.hourly.forEach((r) => {\n        const cell = ensure(r.day, r.hour)\n        cell.answers += r.answers\n        cell.threads += r.threads\n      })\n      this.hourlyClosed.forEach((r) => {\n        ensure(r.day, r.hour).closed += r.closed\n      })\n      return map\n    },\n    heatmapDays() {\n      // Перечисляем ВСЕ дни выбранного периода (включая пустые) — чтобы\n      // дни без активности тоже отображались строкой с точками. Без этого\n      // выходные/отпуска саппорта молча выпадают из таблицы и нельзя\n      // понять, был это пропуск или просто нет данных.\n      if (!this.period || !this.period.from_iso || !this.period.to_iso) {\n        return []\n      }\n      const days = []\n      // UTC, чтобы не словить смещение часового пояса на границе суток.\n      const from = new Date(this.period.from_iso + \'T00:00:00Z\')\n      const to   = new Date(this.period.to_iso   + \'T00:00:00Z\')\n      for (const d = new Date(from); d <= to; d.setUTCDate(d.getUTCDate() + 1)) {\n        days.push(d.toISOString().slice(0, 10))\n      }\n      // DESC — сегодня вверху, старые ниже.\n      return days.reverse()\n    },\n    /**\n     * Развёртка `heatmapDays` в строки таблицы с разделителями-месяцами.\n     * Заголовок-месяц вставляется только если в выборке больше одного месяца —\n     * иначе он избыточен.\n     *\n     * @returns {Array<{type:\'month\'|\'day\', key:string, day?:string, label?:string, daysCount?:number}>}\n     */\n    heatmapRows() {\n      const days = this.heatmapDays\n      if (days.length === 0) return []\n      const months = new Set(days.map((d) => d.slice(0, 7)))\n      const showMonthHeaders = months.size > 1\n\n      const rows = []\n      const daysCountByMonth = {}\n      days.forEach((d) => {\n        const m = d.slice(0, 7)\n        daysCountByMonth[m] = (daysCountByMonth[m] || 0) + 1\n      })\n\n      let prevMonth = null\n      days.forEach((d) => {\n        const m = d.slice(0, 7)\n        if (showMonthHeaders && m !== prevMonth) {\n          rows.push({\n            type: \'month\',\n            key: `m-${m}`,\n            label: this.formatMonth(m),\n            daysCount: daysCountByMonth[m],\n          })\n          prevMonth = m\n        }\n        rows.push({ type: \'day\', key: `d-${d}`, day: d })\n      })\n      return rows\n    },\n    /** Максимум по «ответам» — для расчёта градиента цвета ячейки. */\n    maxCellValue() {\n      let max = 0\n      Object.values(this.heatmap).forEac
2h((row) => {\n        Object.values(row).forEach((v) => {\n          if (v.answers > max) max = v.answers\n        })\n      })\n      return max\n    },\n    /** Серии для графика: A/B/C, агрегированные по дням. */\n    chartSeries() {\n      const dailyAnswers = {}\n      const dailyThreads = {}\n      const dailyClosed = {}\n      this.hourly.forEach((r) => {\n        dailyAnswers[r.day] = (dailyAnswers[r.day] || 0) + r.answers\n        dailyThreads[r.day] = (dailyThreads[r.day] || 0) + r.threads\n      })\n      this.hourlyClosed.forEach((r) => {\n        dailyClosed[r.day] = (dailyClosed[r.day] || 0) + r.closed\n      })\n      const labels = this.heatmapDays\n      return {\n        labels,\n        answers: labels.map((d) => dailyAnswers[d] || 0),\n        threads: labels.map((d) => dailyThreads[d] || 0),\n        closed: labels.map((d) => dailyClosed[d] || 0),\n      }\n    },\n  },\n  mounted() {\n    this.loadData()\n  },\n  watch: {\n    view() {\n      if (this.view === \'chart\') {\n        this.$nextTick(() => this.renderChart())\n      } else {\n        this.$nextTick(() => this.measureHeatmapWidth())\n      }\n    },\n    sortedSummary() {\n      this.$nextTick(() => this.measureHeatmapWidth())\n    },\n    heatmapDays() {\n      this.$nextTick(() => this.measureHeatmapWidth())\n    },\n  },\n  methods: {\n    /** Сокращение для translate с префиксом раздела. */\n    t(key, vars) {\n      return translate(TRANSLATION_ROOT + key, vars)\n    },\n    /** Замер ширины таблицы heatmap — для верхнего scroll-stub\'а. */\n    measureHeatmapWidth() {\n      if (this.$refs.heatmapTable) {\n        this.heatmapWidth = this.$refs.heatmapTable.scrollWidth\n      }\n    },\n    /**\n     * Двусторонний синхрон скролла: при движении одной полосы зеркально\n     * двигаем вторую. Флаг `_syncing` гасит event-эхо.\n     */\n    syncHeatmapScroll(source) {\n      if (this._syncing) {\n        this._syncing = false\n        return\n      }\n      this._syncing = true\n      const top = this.$refs.heatmapTopScroll\n      const bot = this.$refs.heatmapTableScroll\n      if (!top || !bot) return\n      if (source === \'top\') bot.scrollLeft = top.scrollLeft\n      else top.scrollLeft = bot.scrollLeft\n    },\n    async clearCache() {\n      try {\n        await axios.post(\'/e/reports/support_stats/cache/clear\', null, {\n          params: {\n            from: this.filters.from,\n            to: this.filters.to,\n            support: this.filters.support,\n            kind: this.filters.kind,\n          },\n        })\n      } catch (e) {\n        // не критично — если очистка не прошла, просто перезапрашиваем\n      }\n      this.loadData()\n    },\n    setQuick(period) {\n      const to = new Date()\n      const from = new Date()\n      if (period === \'today\') { /* from = today */ }\n      else if (period === \'week\') from.setDate(to.getDate() - 6)\n      else if (period === \'month\') from.setMonth(to.getMonth() - 1)\n      else if (period === \'quarter\') from.setMonth(to.getMonth() - 3)\n      else if (period === \'year\') from.setFullYear(to.getFullYear() - 1)\n      this.filters.from = from.toISOString().slice(0, 10)\n      this.filters.to = to.toISOString().slice(0, 10)\n      this.loadData()\n    },\n    async loadData() {\n      this.loading = true\n      this.error = null\n      try {\n        const { data } = await axios.get(\'/e/reports/support_stats/api\', {\n          params: {\n            from: this.filters.from,\n            to: this.filters.to,\n            support: this.filters.support,\n            kind: this.filters.kind,\n          },\n        })\n        this.summary = data.summary || []\n        this.hourly = data.hourly || []\n        this.hourlyClosed = data.hourly_closed || []\n        this.period = data.period || null\n        if (Array.isArray(data.supports) && data.supports.length) {\n          // Полный список саппортов приходит при каждом запросе, Ð
2½Ð¾ обновляем\n          // его только когда непустой — чтобы при ошибке/пустом ответе селект\n          // не лишился вариантов.\n          this.allSupports = data.supports\n        }\n        // dailyThreads[support_id][day] = threads (корректный distinct по дню)\n        const dt = {}\n        ;(data.daily_threads || []).forEach((r) => {\n          if (!dt[r.support_id]) dt[r.support_id] = {}\n          dt[r.support_id][r.day] = r.threads\n        })\n        this.dailyThreads = dt\n        if (this.view === \'chart\') {\n          this.$nextTick(() => this.renderChart())\n        }\n      } catch (e) {\n        this.error = e.response?.data?.error || this.t(\'errorLoad\')\n      } finally {\n        this.loading = false\n      }\n    },\n    /** \'YYYY-MM\' → «Июнь 2026» (русские месяцы). */\n    formatMonth(ym) {\n      const months = [\n        \'Январь\', \'Февраль\', \'Март\', \'Апрель\', \'Май\', \'Июнь\',\n        \'Июль\', \'Август\', \'Сентябрь\', \'Октябрь\', \'Ноябрь\', \'Декабрь\',\n      ]\n      const [y, m] = ym.split(\'-\')\n      return `${months[parseInt(m, 10) - 1]} ${y}`\n    },\n    /** Склонение «день/дня/дней» (или ru-аналоги в других локалях). */\n    pluralDays(n) {\n      const mod10 = n % 10\n      const mod100 = n % 100\n      if (mod100 >= 11 && mod100 <= 19) return this.t(\'days.many\')\n      if (mod10 === 1) return this.t(\'days.one\')\n      if (mod10 >= 2 && mod10 <= 4) return this.t(\'days.few\')\n      return this.t(\'days.many\')\n    },\n    /**\n     * Клик по заголовку: повторный клик по той же колонке — переключение\n     * направления, клик по другой — выбор и сброс на DESC (числа сверху).\n     */\n    toggleSort(field) {\n      if (this.sortBy === field) {\n        this.sortDir = this.sortDir === \'asc\' ? \'desc\' : \'asc\'\n      } else {\n        this.sortBy = field\n        this.sortDir = \'desc\'\n      }\n    },\n    /** Стрелка-индикатор сортировки для заголовка. */\n    sortInd(field) {\n      if (this.sortBy !== field) return \'\'\n      return this.sortDir === \'asc\' ? \' ↑\' : \' ↓\'\n    },\n    /** CSS-класс для Net-рейтинга: красный <0, серый 0, зелёный >0. */\n    rateNetClass(value) {\n      if (value > 0) return \'text-success\'\n      if (value < 0) return \'text-danger\'\n      return \'text-muted\'\n    },\n    /** Все три метрики в конкретной ячейке (или нули, если пусто). */\n    cellAt(day, hour) {\n      return this.heatmap[day]?.[hour] || { answers: 0, threads: 0, closed: 0 }\n    },\n    cellTitle(day, hour) {\n      const c = this.cellAt(day, hour)\n      // Используем placeholder-замену вручную — наш translate() не делает interpolate сам.\n      return this.t(\'heatmap.cellTooltip\')\n        .replace(\'{answers}\', c.answers)\n        .replace(\'{threads}\', c.threads)\n        .replace(\'{closed}\', c.closed)\n    },\n    /**\n     * Сумма за день. Ответов и Закрыто — обычная сумма по часам (валидно).\n     * Тредов — берём из отдельного daily-COUNT(DISTINCT parent) (см. сервис):\n     * сумма часовых COUNT(DISTINCT) задвоит тред, в который саппорт писал в\n     * разные часы. По-настоящему уникальных тредов за день меньше.\n     */\n    daySum(day) {\n      const row = this.heatmap[day] || {}\n      const sum = { answers: 0, threads: 0, closed: 0 }
2\n      Object.values(row).forEach((c) => {\n        sum.answers += c.answers\n        sum.closed += c.closed\n      })\n      // Треды — из dailyThreads (объединяем по саппортам, если выбрано «все»).\n      Object.values(this.dailyThreads).forEach((perSupport) => {\n        if (perSupport[day]) sum.threads += perSupport[day]\n      })\n      return sum\n    },\n    cellStyle(value) {\n      if (!value || !this.maxCellValue) return {}\n      // Градиент очень светлый — фон только подсказывает интенсивность,\n      // а цифры (тёмные с белым text-shadow) остаются хорошо читаемыми.\n      const ratio = value / this.maxCellValue\n      const alpha = 0.06 + ratio * 0.28\n      return { backgroundColor: `rgba(45, 122, 74, ${alpha.toFixed(2)})` }\n    },\n    renderChart() {\n      if (!this.$refs.chartCanvas) return\n      if (this.chart) {\n        this.chart.destroy()\n        this.chart = null\n      }\n      // Три линии — все три метрики по дням, суммарно по саппортам в выборке.\n      const s = this.chartSeries\n      this.chart = new Chart(this.$refs.chartCanvas.getContext(\'2d\'), {\n        type: \'line\',\n        data: {\n          labels: s.labels,\n          datasets: [\n            {\n              label: this.t(\'chart.answers\'),\n              data: s.answers,\n              borderColor: \'rgba(45, 122, 74, 1)\',\n              backgroundColor: \'rgba(45, 122, 74, 0.15)\',\n              borderWidth: 2,\n              tension: 0.25,\n              fill: false,\n            },\n            {\n              label: this.t(\'chart.threads\'),\n              data: s.threads,\n              borderColor: \'rgba(0, 120, 231, 1)\',\n              backgroundColor: \'rgba(0, 120, 231, 0.15)\',\n              borderWidth: 2,\n              tension: 0.25,\n              fill: false,\n            },\n            {\n              label: this.t(\'chart.closed\'),\n              data: s.closed,\n              borderColor: \'rgba(255, 153, 0, 1)\',\n              backgroundColor: \'rgba(255, 153, 0, 0.15)\',\n              borderWidth: 2,\n              tension: 0.25,\n              fill: false,\n            },\n          ],\n        },\n        options: {\n          responsive: true,\n          interaction: { mode: \'index\', intersect: false },\n          plugins: { legend: { display: true, position: \'top\' } },\n          scales: { y: { beginAtZero: true, ticks: { precision: 0 } } },\n        },\n      })\n    },\n  },\n  beforeUnmount() {\n    if (this.chart) this.chart.destroy()\n  },\n})\n<\/script>\n\n<style scoped>\n.support-heatmap th,\n.support-heatmap td {\n  padding: 4px 6px;\n  font-size: 12px;\n  min-width: 44px;\n  white-space: nowrap;\n}\n.support-heatmap .cell-triple {\n  display: inline-block;\n  line-height: 1.1;\n  font-weight: 700;\n  text-shadow: 0 0 2px rgba(255, 255, 255, 0.85);\n}\n/* Насыщенные цвета — для контраста на зелёном фоне градиента (фон сам по\n   себе светлый, alpha ≤ 0.35, см. cellStyle), плюс белый text-shadow. */\n.support-heatmap .cell-a { color: #1a5c34; }   /* зелёный — ответов */\n.support-heatmap .cell-b { color: #004e96; }   /* синий — тредов */\n.support-heatmap .cell-c { color: #b35e00; }   /* оранжевый — закрыто */\n.support-heatmap .sep    { color: #888; margin: 0 1px; font-weight: 400; }\n.support-heatmap .month-row td {\n  background: #eaf2ec;\n  color: #1a5c34;\n  font-size: 13px;\n  padding: 4px 8px !important;\n  border-top: 1px solid #cfdfd4;\n  border-bottom: 1px solid #cfdfd4;\n}\n.support-heatmap .month-row td b { font-size: 14px; }\n.support-heatmap .month-row td small { color: #6b8b76 !important; }\n.chart-wrap {\n  max-width: 100%;\n  background: #fff;\n  padding: 12px;\n  border: 1px solid #dee2e6;\n  border-radius: 4px;\n}\n.support-heatmap td:first-child {\n  white-space: nowrap;\n}\n.metrics-legend {\n  background: #f5f7f5;\n  border-left: 3px solid #2d7a4a;\n  padding: 10px 14px;\n  font-size: 13px;\n}\n.metrics-legend ul {\n  padding-left: 18px;\n  margin-top: 6px;\n}\n.metrics-legend li {\n  margin-bottom: 4px;\n}\n.metrics-legend code {\n  background: #e9ecef;\n  padding: 0 4px;\n  border-radius: 3px;\n  font-size: 12px;\n}\n.support-stats-report .loading-state {\n  background: #f8f9fa;\n  border: 1px solid #e9ecef;\n  border-radius: 4px;\n}\n/* Отступ между группой «Быстрые периоды» и «Обновить/Очистить кеш» в одной\n   flex-строке (без визуального разделителя). */\n.support-stats-report .filter-divider {\n  display: inline-block;\n  width: 16px;\n}\n.support-stats-report th.sortable {\n  cursor: pointer;\n  user-select: none;\n}\n.support-stats-report th.sortable:hover {\n  background: #f5f5f5;\n}
2\n.support-stats-report .sort-ind {\n  color: #2d7a4a;\n  font-weight: 700;\n  margin-left: 2px;\n}\n/* Верхний scrollbar над heatmap. Высота ~15px — стандартная толщина системного\n   скроллбара. Внутри пустой `<div>` ровно той же ширины что и таблица — это\n   создаёт горизонтальный overflow и заставляет браузер отрисовать scrollbar. */\n.support-stats-report .heatmap-top-scroll {\n  overflow-x: auto;\n  overflow-y: hidden;\n  margin-bottom: 4px;\n  height: 15px;\n}\n</style>\n'],sourceRoot:""}]);const i=l},1966:(e,n,r)=>{r.r(n),r.d(n,{default:()=>me});var a=r(88231),s=function _withScopeId(e){return(0,a.Qi)("data-v-22404036"),e=e(),(0,a.jt)(),e},o={class:"support-stats-report"},l={class:"mb-3"},i={class:"filter-section mb-4"},c={class:"row"},d={class:"col-md-2 col-sm-6 mb-3"},u={class:"form-label"},p={class:"col-md-2 col-sm-6 mb-3"},h={class:"form-label"},m={class:"col-md-3 col-sm-6 mb-3"},f={class:"form-label"},y={value:-1},b=["value"],v={class:"col-md-5 col-sm-12 mb-3"},g={class:"form-label"},A={class:"d-flex flex-wrap align-items-center"},k=s((function(){return(0,a.Lk)("span",{class:"filter-divider mb-2","aria-hidden":"true"},null,-1)})),w=["disabled"],C={key:0},x={key:1},_=["disabled","title"],L={class:"row"},E={class:"col-md-12 mb-2"},S={class:"form-label fw-bold me-3"},B={class:"btn-group",role:"group"},T=["onClick"],D={key:0,class:"loading-state d-flex align-items-center justify-content-center py-5"},I=s((function(){return(0,a.Lk)("div",{class:"spinner-border text-primary me-3",role:"status","aria-hidden":"true"},null,-1)})),M={class:"fw-bold"},O={class:"text-muted small"},F={key:1,class:"alert alert-danger"},W={key:2,class:"text-muted small mb-2"},H=s((function(){return(0,a.Lk)("i",{class:"fa fa-database me-1"},null,-1)})),R={class:"mt-4 mb-2"},j={class:"metrics-legend mb-3"},X={class:"mb-0"},N=["innerHTML"],G=["innerHTML"],$=["innerHTML"],P=["innerHTML"],Q=["innerHTML"],z=["innerHTML"],K=["innerHTML"],U=["innerHTML"],Y={class:"table-responsive mb-4"},q={class:"table table-bordered table-striped table-sm"},V=s((function(){return(0,a.Lk)("th",null,"#",-1)})),Z={class:"sort-ind"},J={class:"sort-ind"},tt={class:"sort-ind"},et={class:"sort-ind"},nt={class:"sort-ind"},rt={class:"sort-ind"},at=["title"],st={class:"sort-ind"},ot=["title"],lt={class:"sort-ind"},it=["title"],ct={class:"sort-ind"},dt=["title"],ut={class:"sort-ind"},pt={class:"text-right"},ht={class:"text-right"},mt={class:"text-right"},ft={class:"text-right text-success"},yt={class:"text-right text-danger"},bt={class:"text-right"},vt={key:0},gt={key:1,class:"text-muted"},At={class:"text-right"},kt={key:0},wt={key:1,class:"text-muted"},Ct={class:"text-right"},xt={key:0},_t={key:1,class:"text-muted"},Lt={class:"text-right"},Et={key:1,class:"text-muted"},St={key:0},Bt={colspan:"11",class:"text-center text-muted"},Tt={class:"mt-4 mb-2 d-flex align-items-center flex-wrap"},Dt={class:"btn-group ms-3",role:"group"},It={class:"text-muted small mb-2"},Mt=["innerHTML"],Ot=s((function(){return(0,a.Lk)("br",null,null,-1)})),Ft=["innerHTML"],Wt={ref:"heatmapTable",class:"table table-bordered table-sm support-heatmap"},Ht={class:"text-center"},Rt={key:0,class:"month-row"},jt={colspan:"26"},Xt={class:"text-muted ms-2"},Nt={key:1},Gt=["title"],$t={key:0,class:"cell-triple"},Pt={class:"cell-a"},Qt=s((function(){return(0,a.Lk)("span",{class:"sep"},"/",-1)})),zt={class:"cell-b"},Kt=s((function(){return(0,a.Lk)("span",{class:"sep"},"/",-1)})),Ut={class:"cell-c"},Yt={key:1,class:"text-muted"},qt={class:"text-center"},Vt={class:"cell-triple"},Zt={class:"cell-a"},Jt=s((function(){return(0,a.Lk)("span",{class:"sep"},"/",-1)})),te={class:"cell-b"},ee=s((function(){return(0,a.Lk)("span",{class:"sep"},"/",-1)})),ne={class:"cell-c"},re={key:0},ae={colspan:"26",class:"text-center text-muted"},se={key:5,class:"chart-wrap"},oe={ref:"chartCanvas"};var le=r(72505),ie=r.n(le),ce=r(70106),de=r(79119);function _typeof(e){return _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_typeof(e)}function _slicedToArray(e,n){return function _arrayWithHoles(e){if(Array.isArray(e))return e}(e)||function _iterableToArrayLimit(e,n){var r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var a,s,o,l,i=[],c=!0,d=!1;try{if(o=(r=r.call(e)).next,0===n){if(Object(r)!==r)return;c=!1}else for(;!(c=(a=o.call(r)).done)&&(i.push(a.value),i.length!==n);c=!0);}catch(e){d=!0,s=e}
vendor: 5,815 bytes, line 2
2finally{try{if(!c&&null!=r.return&&(l=r.return(),Object(l)!==l))return}finally{if(d)throw s}}return i}}(e,n)||_unsupportedIterableToArray(e,n)||function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _regeneratorRuntime(){_regeneratorRuntime=function _regeneratorRuntime(){return n};var e,n={},r=Object.prototype,a=r.hasOwnProperty,s=Object.defineProperty||function(e,n,r){e[n]=r.value},o="function"==typeof Symbol?Symbol:{},l=o.iterator||"@@iterator",i=o.asyncIterator||"@@asyncIterator",c=o.toStringTag||"@@toStringTag";function define(e,n,r){return Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}),e[n]}try{define({},"")}catch(e){define=function define(e,n,r){return e[n]=r}}function wrap(e,n,r,a){var o=n&&n.prototype instanceof Generator?n:Generator,l=Object.create(o.prototype),i=new Context(a||[]);return s(l,"_invoke",{value:makeInvokeMethod(e,r,i)}),l}function tryCatch(e,n,r){try{return{type:"normal",arg:e.call(n,r)}}catch(e){return{type:"throw",arg:e}}}n.wrap=wrap;var d="suspendedStart",u="suspendedYield",p="executing",h="completed",m={};function Generator(){}function GeneratorFunction(){}function GeneratorFunctionPrototype(){}var f={};define(f,l,(function(){return this}));var y=Object.getPrototypeOf,b=y&&y(y(values([])));b&&b!==r&&a.call(b,l)&&(f=b);var v=GeneratorFunctionPrototype.prototype=Generator.prototype=Object.create(f);function defineIteratorMethods(e){["next","throw","return"].forEach((function(n){define(e,n,(function(e){return this._invoke(n,e)}))}))}function AsyncIterator(e,n){function invoke(r,s,o,l){var i=tryCatch(e[r],e,s);if("throw"!==i.type){var c=i.arg,d=c.value;return d&&"object"==_typeof(d)&&a.call(d,"__await")?n.resolve(d.__await).then((function(e){invoke("next",e,o,l)}),(function(e){invoke("throw",e,o,l)})):n.resolve(d).then((function(e){c.value=e,o(c)}),(function(e){return invoke("throw",e,o,l)}))}l(i.arg)}var r;s(this,"_invoke",{value:function value(e,a){function callInvokeWithMethodAndArg(){return new n((function(n,r){invoke(e,a,n,r)}))}return r=r?r.then(callInvokeWithMethodAndArg,callInvokeWithMethodAndArg):callInvokeWithMethodAndArg()}})}function makeInvokeMethod(n,r,a){var s=d;return function(o,l){if(s===p)throw Error("Generator is already running");if(s===h){if("throw"===o)throw l;return{value:e,done:!0}}for(a.method=o,a.arg=l;;){var i=a.delegate;if(i){var c=maybeInvokeDelegate(i,a);if(c){if(c===m)continue;return c}}if("next"===a.method)a.sent=a._sent=a.arg;else if("throw"===a.method){if(s===d)throw s=h,a.arg;a.dispatchException(a.arg)}else"return"===a.method&&a.abrupt("return",a.arg);s=p;var f=tryCatch(n,r,a);if("normal"===f.type){if(s=a.done?h:u,f.arg===m)continue;return{value:f.arg,done:a.done}}"throw"===f.type&&(s=h,a.method="throw",a.arg=f.arg)}}}function maybeInvokeDelegate(n,r){var a=r.method,s=n.iterator[a];if(s===e)return r.delegate=null,"throw"===a&&n.iterator.return&&(r.method="return",r.arg=e,maybeInvokeDelegate(n,r),"throw"===r.method)||"return"!==a&&(r.method="throw",r.arg=new TypeError("The iterator does not provide a '"+a+"' method")),m;var o=tryCatch(s,n.iterator,r.arg);if("throw"===o.type)return r.method="throw",r.arg=o.arg,r.delegate=null,m;var l=o.arg;return l?l.done?(r[n.resultName]=l.value,r.next=n.nextLoc,"return"!==r.method&&(r.method="next",r.arg=e),r.delegate=null,m):l:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,m)}function pushTryEntry(e){var n={tryLoc:e[0]};1 in e&&(n.catchLoc=e[1]),2 in e&&(n.finallyLoc=e[2],n.afterLoc=e[3]),this.tryEntries.push(n)}function resetTryEntry(e){var n=e.completion||{};n.type="normal",delete n.arg,e.completion=n}function Context(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(pushTryEntry,this),this.reset(!0)}function values(n){if(n||""===n){var r=n[l];if(r)return r.call(n);if("function"==typeof n.next)return n;if(!isNaN(n.length)){var s=-1,o=function next(){for(;++s<n.length;)if(a.call(n,s))return next.value=n[s],next.done=!1,next;return next.value=e,next.done=!0,next};return o.next=o}}throw new TypeError(_typeof(n)+" is not iterable")}return GeneratorFunction.prototype=GeneratorFunctionPrototype,s(v,"constructor",{value:GeneratorFunctionPrototype,configurable:!0}),s(GeneratorFunctionPrototype,"constructor",{value:GeneratorFunction,configurable:!0}),GeneratorFunction.displayName=define(GeneratorFunctionPrototype,c,"GeneratorFunction"),n.isGeneratorFunction=function(e){var n="function"==typeof e&&e.constructor;return!!n&&(n===GeneratorFunction||"GeneratorFunction"===(n.displayName||n.name))},n.mark=function(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,GeneratorFunctionPrototype):(e.__proto__=GeneratorFunctionPrototype,define(e,c,"GeneratorFunction")),e.prototype=Object.create(v),e},n.awrap=function(e){return{__await:e}},defineIteratorMethods(AsyncIterator.prototype),define(AsyncIterator.prototype,i,(function(){return this})),n.AsyncIterator=AsyncIterator,n.async=function(e,r,a,s,o){void 0===o&&(o=Promise);var l=new AsyncIterator(wrap(e,r,a,s),o);return n.isGeneratorFunction(r)?l:l.next().then((function(e){return e.done?e.value:l.next()}))},defineIteratorMethods(v),define(v,c,"Generator"),define(v,l,(function(){return this})),define(v,"toString",(function(){return"[object Generator]"})),n.keys=function(e){var n=Object(e),r=[];for(var a in n)r.push(a);return r.reverse(),function next(){for(;r.length;){var e=r.pop();if(e in n)return next.value=e,next.done=!1,next}return next.done=!0,next}},n.values=values,Context.prototype={constructor:Context,reset:function reset(n){if(this.prev=0,this.next=0,this.sent=this._sent=e,this.done=!1,this.delegate=null,this.method="next",this.arg=e,this.tryEntries.forEac
2h(resetTryEntry),!n)for(var r in this)"t"===r.charAt(0)&&a.call(this,r)&&!isNaN(+r.slice(1))&&(this[r]=e)},stop:function stop(){this.done=!0;var e=this.tryEntries[0].completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function dispatchException(n){if(this.done)throw n;var r=this;function handle(a,s){return l.type="throw",l.arg=n,r.next=a,s&&(r.method="next",r.arg=e),!!s}for(var s=this.tryEntries.length-1;s>=0;--s){var o=this.tryEntries[s],l=o.completion;if("root"===o.tryLoc)return handle("end");if(o.tryLoc<=this.prev){var i=a.call(o,"catchLoc"),c=a.call(o,"finallyLoc");if(i&&c){if(this.prev<o.catchLoc)return handle(o.catchLoc,!0);if(this.prev<o.finallyLoc)return handle(o.finallyLoc)}else if(i){if(this.prev<o.catchLoc)return handle(o.catchLoc,!0)}else{if(!c)throw Error("try statement without catch or finally");if(this.prev<o.finallyLoc)return handle(o.finallyLoc)}}}},abrupt:function abrupt(e,n){for(var r=this.tryEntries.length-1;r>=0;--r){var s=this.tryEntries[r];if(s.tryLoc<=this.prev&&a.call(s,"finallyLoc")&&this.prev<s.finallyLoc){var o=s;break}}o&&("break"===e||"continue"===e)&&o.tryLoc<=n&&n<=o.finallyLoc&&(o=null);var l=o?o.completion:{};return l.type=e,l.arg=n,o?(this.method="next",this.next=o.finallyLoc,m):this.complete(l)},complete:function complete(e,n){if("throw"===e.type)throw e.arg;return"break"===e.type||"continue"===e.type?this.next=e.arg:"return"===e.type?(this.rval=this.arg=e.arg,this.method="return",this.next="end"):"normal"===e.type&&n&&(this.next=n),m},finish:function finish(e){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),resetTryEntry(r),m}},catch:function _catch(e){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.tryLoc===e){var a=r.completion;if("throw"===a.type){var s=a.arg;resetTryEntry(r)}return s}}throw Error("illegal catch attempt")},delegateYield:function delegateYield(n,r,a){return this.delegate={iterator:values(n),resultName:r,nextLoc:a},"next"===this.method&&(this.arg=e),m}},n}function asyncGeneratorStep(e,n,r,a,s,o,l){try{var i=e[o](l),c=i.value}catch(e){return void r(e)}i.done?n(c):Promise.resolve(c).then(a,s)}function _asyncToGenerator(e){return function(){var n=this,r=arguments;return new Promise((function(a,s){var o=e.apply(n,r);function _next(e){asyncGeneratorStep(o,a,s,_next,_throw,"next",e)}function _throw(e){asyncGeneratorStep(o,a,s,_next,_throw,"throw",e)}_next(void 0)}))}}function _toConsumableArray(e){return function _arrayWithoutHoles(e){if(Array.isArray(e))return _arrayLikeToArray(e)}(e)||function _iterableToArray(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||_unsupportedIterableToArray(e)||function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function _unsupportedIterableToArray(e,n){if(e){if("string"==typeof e)return _arrayLikeToArray(e,n);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?_arrayLikeToArray(e,n):void 0}}function _arrayLikeToArray(e,n){(null==n||n>e.length)&&(n=e.length);for(var r=0,a=Array(n);r<n;r++)a[r]=e[r];return a}ce.Chart.register.apply(ce.Chart,_toConsumableArray(ce.registerables));var ue=function todayIso(){return(new Date).toISOString().slice(0,10)},pe=function monthStartIso(){var e=new Date;return e.setDate(1),e.toISOString().slice(0,10)};const he=(0,a.pM)({name:"SupportStatsReport",data:function data(){return{filters:{from:pe(),to:ue(),support:-1,kind:"all"},kindOptions:[{code:"all",labelKey:"filters.kindAll"},{code:"ticket",labelKey:"filters.kindTickets"},{code:"chat",labelKey:"filters.kindChats"}],summary:[],hourly:[],hourlyClosed:[],dailyThreads:{},allSupports:[],period:null,loading:!1,sortBy:"answers",sortDir:"desc",heatmapWidth:0,_syncing:!1,error:null,view:"table",chart:null}},computed:{sortedSummary:function sortedSummary(){var e=this.sortBy,n="asc"===this.sortDir?1:-1;return _toConsumableArray(this.summary).sort((function(r,a){var s=r[e],o=a[e];return null===s&&null===o?0:null===s?1:null===o?-1:"string"==typeof s?s.localeCompare(o)*n:(s-o)*n}))},heatmap:function heatmap(){var e={},n=function ensure(n,r){return e[n]||(e[n]={}),e[n][r]||(e[n][r]={answers:0,threads:0,closed:0}),e[n][r]};
2return this.hourly.forEach((function(e){var r=n(e.day,e.hour);r.answers+=e.answers,r.threads+=e.threads})),this.hourlyClosed.forEach((function(e){n(e.day,e.hour).closed+=e.closed})),e},heatmapDays:function heatmapDays(){if(!this.period||!this.period.from_iso||!this.period.to_iso)return[];for(var e=[],n=new Date(this.period.from_iso+"T00:00:00Z"),r=new Date(this.period.to_iso+"T00:00:00Z"),a=new Date(n);a<=r;a.setUTCDate(a.getUTCDate()+1))e.push(a.toISOString().slice(0,10));return e.reverse()},heatmapRows:function heatmapRows(){var e=this,n=this.heatmapDays;if(0===n.length)return[];var r=new Set(n.map((function(e){return e.slice(0,7)}))).size>1,a=[],s={};n.forEach((function(e){var n=e.slice(0,7);s[n]=(s[n]||0)+1}));var o=null;return n.forEach((function(n){var l=n.slice(0,7);r&&l!==o&&(a.push({type:"month",key:"m-".concat(l),label:e.formatMonth(l),daysCount:s[l]}),o=l),a.push({type:"day",key:"d-".concat(n),day:n})})),a},maxCellValue:function maxCellValue(){var e=0;return Object.values(this.heatmap).forEach((function(n){Object.values(n).forEach((function(n){n.answers>e&&(e=n.answers)}))})),e},chartSeries:function chartSeries(){var e={},n={},r={};this.hourly.forEach((function(r){e[r.day]=(e[r.day]||0)+r.answers,n[r.day]=(n[r.day]||0)+r.threads})),this.hourlyClosed.forEach((function(e){r[e.day]=(r[e.day]||0)+e.closed}));var a=this.heatmapDays;return{labels:a,answers:a.map((function(n){return e[n]||0})),threads:a.map((function(e){return n[e]||0})),closed:a.map((function(e){return r[e]||0}))}}},mounted:function mounted(){this.loadData()},watch:{view:function view(){var e=this;"chart"===this.view?this.$nextTick((function(){return e.renderChart()})):this.$nextTick((function(){return e.measureHeatmapWidth()}))},sortedSummary:function sortedSummary(){var e=this;this.$nextTick((function(){return e.measureHeatmapWidth()}))},heatmapDays:function heatmapDays(){var e=this;this.$nextTick((function(){return e.measureHeatmapWidth()}))}},methods:{t:function t(e,n){return(0,de.A)("message.admin.reports.supportStats."+e,n)},measureHeatmapWidth:function measureHeatmapWidth(){this.$refs.heatmapTable&&(this.heatmapWidth=this.$refs.heatmapTable.scrollWidth)},syncHeatmapScroll:function syncHeatmapScroll(e){if(this._syncing)this._syncing=!1;else{this._syncing=!0;var n=this.$refs.heatmapTopScroll,r=this.$refs.heatmapTableScroll;n&&r&&("top"===e?r.scrollLeft=n.scrollLeft:n.scrollLeft=r.scrollLeft)}},clearCache:function clearCache(){var e=this;return _asyncToGenerator(_regeneratorRuntime().mark((function _callee(){return _regeneratorRuntime().wrap((function _callee$(n){for(;;)switch(n.prev=n.next){case 0:return n.prev=0,n.next=3,ie().post("/e/reports/support_stats/cache/clear",null,{params:{from:e.filters.from,to:e.filters.to,support:e.filters.support,kind:e.filters.kind}});case 3:n.next=7;break;case 5:n.prev=5,n.t0=n.catch(0);case 7:e.loadData();case 8:case"end":return n.stop()}}),_callee,null,[[0,5]])})))()},setQuick:function setQuick(e){var n=new Date,r=new Date;"today"===e||("week"===e?r.setDate(n.getDate()-6):"month"===e?r.setMonth(n.getMonth()-1):"quarter"===e?r.setMonth(n.getMonth()-3):"year"===e&&r.setFullYear(n.getFullYear()-1)),this.filters.from=r.toISOString().slice(0,10),this.filters.to=n.toISOString().slice(0,10),this.loadData()},loadData:function loadData(){var e=this;return _asyncToGenerator(_regeneratorRuntime().mark((function _callee2(){var n,r,a,s;return _regeneratorRuntime().wrap((function _callee2$(o){for(;;)switch(o.prev=o.next){case 0:return e.loading=!0,e.error=null,o.prev=2,o.next=5,ie().get("/e/reports/support_stats/api",{params:{from:e.filters.from,to:e.filters.to,support:e.filters.support,kind:e.filters.kind}});case 5:n=o.sent,r=n.data,e.summary=r.summary||[],e.hourly=r.hourly||[],e.hourlyClosed=r.hourly_closed||[],e.period=r.period||null,Array.isArray(r.supports)&&r.supports.length&&(e.allSupports=r.supports),a={},(r.daily_threads||[]).forEach((function(e){a[e.support_id]||(a[e.support_id]={}),a[e.support_id][e.day]=e.threads})),e.dailyThreads=a,"chart"===e.view&&e.$nextTick((function(){return e.renderChart()})),o.next=21;break;case 18:o.prev=18,o.t0=o.catch(2),e.error=(null===(s=o.t0.response)||void 0===s||null===(s=s.data)||void 0===s?void 0:s.error)||e.t("errorLoad");case 21:return o.prev=21,e.loading=!1,o.finish(21);case 24:case"end":return o.stop()}}),_callee2,null,[[2,18,21,24]])})))()},formatMonth:function formatMonth(e){var n=_slicedToArray(e.split("-"),2),r=n[0],a=n[1];return"".concat(["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"][parseInt(a,10)-1]," ").concat(r)},pluralDays:function pluralDays(e){var n=e%10,r=e%100;return r>=11&&r<=19?this.t("days.many"):1===n?this.t("days.one"):n>=2&&n<=4?this.t("days.few"):this.t("days.many")},toggleSort:function toggleSort(e){this.sortBy===e?this.sortDir="asc"===this.sortDir?"desc":"asc":(this.sortBy=e,this.sortDir="desc")},sortInd:function sortInd(e){return this.sortBy!==e?"":"asc"===this.sortDir?" ↑":" ↓"},rateNetClass:function rateNetClass(e){return e>0?"text-success":e<0?"text-danger":"text-muted"},cellAt:function cellAt(e,n){var r;return(null===(r=this.heatmap[e])||void 0===r?void 0:r[n])||{answers:0,threads:0,closed:0}},cellTitle:function cellTitle(e,n){var r=this.cellAt(e,n);return this.t("heatmap.cellTooltip").replace("{answers}
2",r.answers).replace("{threads}",r.threads).replace("{closed}",r.closed)},daySum:function daySum(e){var n=this.heatmap[e]||{},r={answers:0,threads:0,closed:0};return Object.values(n).forEach((function(e){r.answers+=e.answers,r.closed+=e.closed})),Object.values(this.dailyThreads).forEach((function(n){n[e]&&(r.threads+=n[e])})),r},cellStyle:function cellStyle(e){if(!e||!this.maxCellValue)return{};var n=e/this.maxCellValue;return{backgroundColor:"rgba(45, 122, 74, ".concat((.06+.28*n).toFixed(2),")")}},renderChart:function renderChart(){if(this.$refs.chartCanvas){this.chart&&(this.chart.destroy(),this.chart=null);var e=this.chartSeries;this.chart=new ce.Chart(this.$refs.chartCanvas.getContext("2d"),{type:"line",data:{labels:e.labels,datasets:[{label:this.t("chart.answers"),data:e.answers,borderColor:"rgba(45, 122, 74, 1)",backgroundColor:"rgba(45, 122, 74, 0.15)",borderWidth:2,tension:.25,fill:!1},{label:this.t("chart.threads"),data:e.threads,borderColor:"rgba(0, 120, 231, 1)",backgroundColor:"rgba(0, 120, 231, 0.15)",borderWidth:2,tension:.25,fill:!1},{label:this.t("chart.closed"),data:e.closed,borderColor:"rgba(255, 153, 0, 1)",backgroundColor:"rgba(255, 153, 0, 0.15)",borderWidth:2,tension:.25,fill:!1}]},options:{responsive:!0,interaction:{mode:"index",intersect:!1},plugins:{legend:{display:!0,position:"top"}},scales:{y:{beginAtZero:!0,ticks:{precision:0}}}}})}}},beforeUnmount:function beforeUnmount(){this.chart&&this.chart.destroy()}});r(74785);const me=(0,r(66262).A)(he,[["render",function render(e,n,r,s,le,ie){return(0,a.uX)(),(0,a.CE)("div",o,[(0,a.Lk)("h3",l,(0,a.v_)(e.t("title")),1),(0,a.Lk)("div",i,[(0,a.Lk)("div",c,[(0,a.Lk)("div",d,[(0,a.Lk)("label",u,(0,a.v_)(e.t("filters.dateFrom")),1),(0,a.bo)((0,a.Lk)("input",{type:"date","onUpdate:modelValue":n[0]||(n[0]=function(n){return e.filters.from=n}),class:"form-control",onChange:n[1]||(n[1]=function(){return e.loadData&&e.loadData.apply(e,arguments)})},null,544),[[a.Jo,e.filters.from]])]),(0,a.Lk)("div",p,[(0,a.Lk)("label",h,(0,a.v_)(e.t("filters.dateTo")),1),(0,a.bo)((0,a.Lk)("input",{type:"date","onUpdate:modelValue":n[2]||(n[2]=function(n){return e.filters.to=n}),class:"form-control",onChange:n[3]||(n[3]=function(){return e.loadData&&e.loadData.apply(e,arguments)})},null,544),[[a.Jo,e.filters.to]])]),(0,a.Lk)("div",m,[(0,a.Lk)("label",f,(0,a.v_)(e.t("filters.support")),1),(0,a.bo)((0,a.Lk)("select",{"onUpdate:modelValue":n[4]||(n[4]=function(n){return e.filters.support=n}),class:"form-control",onChange:n[5]||(n[5]=function(){return e.loadData&&e.loadData.apply(e,arguments)})},[(0,a.Lk)("option",y,(0,a.v_)(e.t("filters.allSupports")),1),((0,a.uX)(!0),(0,a.CE)(a.FK,null,(0,a.pI)(e.allSupports,(function(e){return(0,a.uX)(),(0,a.CE)("option",{key:e.id,value:e.id}," #"+(0,a.v_)(e.id)+" — "+(0,a.v_)(e.name),9,b)})),128))],544),[[a.u1,e.filters.support,void 0,{number:!0}]])]),(0,a.Lk)("div",v,[(0,a.Lk)("label",g,(0,a.v_)(e.t("filters.quickPeriods")),1),(0,a.Lk)("div",A,[(0,a.Lk)("button",{type:"button",class:"btn btn-outline-primary me-2 mb-2",onClick:n[6]||(n[6]=function(n){return e.setQuick("today")})},(0,a.v_)(e.t("filters.today")),1),(0,a.Lk)("button",{type:"button",class:"btn btn-outline-primary me-2 mb-2",onClick:n[7]||(n[7]=function(n){return e.setQuick("week")})},(0,a.v_)(e.t("filters.week")),1),(0,a.Lk)("button",{type:"button",class:"btn btn-outline-primary me-2 mb-2",onClick:n[8]||(n[8]=function(n){return e.setQuick("month")})},(0,a.v_)(e.t("filters.month")),1),(0,a.Lk)("button",{type:"button",class:"btn btn-outline-primary me-2 mb-2",onClick:n[9]||(n[9]=function(n){return e.setQuick("quarter")})},(0,a.v_)(e.t("filters.quarter")),1),(0,a.Lk)("button",{type:"button",class:"btn btn-outline-primary me-2 mb-2",onClick:n[10]||(n[10]=function(n){return e.setQuick("year")})},(0,a.v_)(e.t("filters.year")),1),k,(0,a.Lk)("button",{type:"button",class:"btn btn-outline-secondary me-2 mb-2",disabled:e.loading,onClick:n[11]||(n[11]=function(){return e.loadData&&e.loadData.apply(e,arguments)})},[e.loading?((0,a.uX)(),(0,a.CE)("span",C,(0,a.v_)(e.t("filters.loading")),1)):((0,a.uX)(),(0,a.CE)("span",x,(0,a.v_)(e.t("filters.refresh")),1))],8,w),(0,a.Lk)("button",{type:"button",class:"btn btn-outline-warning me-2 mb-2",disabled:e.loading,title:e.t("filters.clearCacheTitle"),onClick:n[12]||(n[12]=function(){return e.clearCache&&e.clearCache.apply(e,arguments)})},(0,a.v_)(e.t("filters.clearCache")),9,_)])])]),(0,a.Lk)("div",L,[(0,a.Lk)("div",E,[(0,a.Lk)("label",S,(0,a.v_)(e.t("filters.kind")),1),(0,a.Lk)("div",B,[((0,a.uX)(!0),(0,a.CE)(a.FK,null,(0,a.pI)(e.kindOptions,(function(n){return(0,a.uX)(),(0,a.CE)("button",{type:"button",class:(0,a.C4)(["btn btn-sm",e.filters.kind===n.code?"btn-primary":"btn-outline-primary"]),key:n.code,onClick:function onClick(r){e.filters.kind=n.code,e.loadData()}},(0,a.v_)(e.t(n.labelKey)),11,T)})),128))])])])]),e.loading?((0,a.uX)(),(0,a.CE)("div",D,[I,(0,a.Lk)("div",null,[(0,a.Lk)("div",M,(0,a.v_)(e.t("filters.loading")),1),(0,a.Lk)("div",O,(0,a.v_)(e.t("loadingHint")),1)])])):(0,a.Q3)("",!0),e.error?((0,a.uX)(),(0,a.CE)("div",F,(0,a.v_)(e.error),1)):(0,a.Q3)("",!0),e.period&&e.period.cached&&!e.loading?((0,a.uX)(),(0,a.CE)("div",W,[H,(0,a.eW)(" "+(0,a.v_)(e.t("fromCache")),1)])):(0,a.Q3)("",!0),(0,a.Lk)("h4",R,(0,a.v_)(e.t("rating")),1),(0,a.Lk)("div",j,[(0,a.Lk)("div",null,[(0,a.Lk)("b",null,(0,a.v_)(e.t("legend.title")),1)]),(0,a.Lk)("ul",X,[(0,a.Lk)("li",{innerHTML:e.t("legend.answers")},null,8,N),(0,a.Lk)("li",{innerHTML:e.t("legend.threads")},null,8,G),(0,a.Lk)("li",{innerHTML:e.t("legend.closed")},null,8,$),(0,a.Lk)("li",{innerHTML:e.t("legend.thanksDislikes")},null,8,P),(0,a.Lk)("li",{innerHTML:e.t("legend.ratedPct")},null,8,Q),(0,a.Lk)("li",{innerHTML:e.t("legend.rateFromAnswers")},null,8,z),(0,a.Lk)("li",{innerHTML:e.t("legend.rateFromRated")}
2,null,8,K),(0,a.Lk)("li",{innerHTML:e.t("legend.rateNet")},null,8,U)])]),(0,a.Lk)("div",Y,[(0,a.Lk)("table",q,[(0,a.Lk)("thead",null,[(0,a.Lk)("tr",null,[V,(0,a.Lk)("th",{class:"sortable",onClick:n[13]||(n[13]=function(n){return e.toggleSort("support_name")})},[(0,a.eW)((0,a.v_)(e.t("table.support")),1),(0,a.Lk)("span",Z,(0,a.v_)(e.sortInd("support_name")),1)]),(0,a.Lk)("th",{class:"text-right sortable",onClick:n[14]||(n[14]=function(n){return e.toggleSort("answers")})},[(0,a.eW)((0,a.v_)(e.t("table.answers")),1),(0,a.Lk)("span",J,(0,a.v_)(e.sortInd("answers")),1)]),(0,a.Lk)("th",{class:"text-right sortable",onClick:n[15]||(n[15]=function(n){return e.toggleSort("threads")})},[(0,a.eW)((0,a.v_)(e.t("table.threads")),1),(0,a.Lk)("span",tt,(0,a.v_)(e.sortInd("threads")),1)]),(0,a.Lk)("th",{class:"text-right sortable",onClick:n[16]||(n[16]=function(n){return e.toggleSort("closed")})},[(0,a.eW)((0,a.v_)(e.t("table.closed")),1),(0,a.Lk)("span",et,(0,a.v_)(e.sortInd("closed")),1)]),(0,a.Lk)("th",{class:"text-right sortable",onClick:n[17]||(n[17]=function(n){return e.toggleSort("thanks_count")})},[(0,a.eW)((0,a.v_)(e.t("table.thanks")),1),(0,a.Lk)("span",nt,(0,a.v_)(e.sortInd("thanks_count")),1)]),(0,a.Lk)("th",{class:"text-right sortable",onClick:n[18]||(n[18]=function(n){return e.toggleSort("dislike_count")})},[(0,a.eW)((0,a.v_)(e.t("table.dislikes")),1),(0,a.Lk)("span",rt,(0,a.v_)(e.sortInd("dislike_count")),1)]),(0,a.Lk)("th",{class:"text-right sortable",title:e.t("table.tooltipRatedPct"),onClick:n[19]||(n[19]=function(n){return e.toggleSort("rated_pct")})},[(0,a.eW)((0,a.v_)(e.t("table.ratedPct")),1),(0,a.Lk)("span",st,(0,a.v_)(e.sortInd("rated_pct")),1)],8,at),(0,a.Lk)("th",{class:"text-right sortable",title:e.t("table.tooltipRateFromAnswers"),onClick:n[20]||(n[20]=function(n){return e.toggleSort("rate_from_answers")})},[(0,a.eW)((0,a.v_)(e.t("table.rateFromAnswers")),1),(0,a.Lk)("span",lt,(0,a.v_)(e.sortInd("rate_from_answers")),1)],8,ot),(0,a.Lk)("th",{class:"text-right sortable",title:e.t("table.tooltipRateFromRated"),onClick:n[21]||(n[21]=function(n){return e.toggleSort("rate_from_rated")})},[(0,a.eW)((0,a.v_)(e.t("table.rateFromRated")),1),(0,a.Lk)("span",ct,(0,a.v_)(e.sortInd("rate_from_rated")),1)],8,it),(0,a.Lk)("th",{class:"text-right sortable",title:e.t("table.tooltipRateNet"),onClick:n[22]||(n[22]=function(n){return e.toggleSort("rate_net")})},[(0,a.eW)((0,a.v_)(e.t("table.rateNet")),1),(0,a.Lk)("span",ut,(0,a.v_)(e.sortInd("rate_net")),1)],8,dt)])]),(0,a.Lk)("tbody",null,[((0,a.uX)(!0),(0,a.CE)(a.FK,null,(0,a.pI)(e.sortedSummary,(function(n,r){return(0,a.uX)(),(0,a.CE)("tr",{key:n.support_id},[(0,a.Lk)("td",null,(0,a.v_)(r+1),1),(0,a.Lk)("td",null," #"+(0,a.v_)(n.support_id)+" — "+(0,a.v_)(n.support_name),1),(0,a.Lk)("td",pt,[(0,a.Lk)("b",null,(0,a.v_)(n.answers),1)]),(0,a.Lk)("td",ht,(0,a.v_)(n.threads),1),(0,a.Lk)("td",mt,(0,a.v_)(n.closed),1),(0,a.Lk)("td",ft,(0,a.v_)(n.thanks_count),1),(0,a.Lk)("td",yt,(0,a.v_)(n.dislike_count),1),(0,a.Lk)("td",bt,[null!==n.rated_pct?((0,a.uX)(),(0,a.CE)("span",vt,(0,a.v_)(n.rated_pct)+"%",1)):((0,a.uX)(),(0,a.CE)("span",gt,"—"))]),(0,a.Lk)("td",At,[null!==n.rate_from_answers?((0,a.uX)(),(0,a.CE)("span",kt,(0,a.v_)(n.rate_from_answers)+"%",1)):((0,a.uX)(),(0,a.CE)("span",wt,"—"))]),(0,a.Lk)("td",Ct,[null!==n.rate_from_rated?((0,a.uX)(),(0,a.CE)("span",xt,(0,a.v_)(n.rate_from_rated)+"%",1)):((0,a.uX)(),(0,a.CE)("span",_t,"—"))]),(0,a.Lk)("td",Lt,[null!==n.rate_net?((0,a.uX)(),(0,a.CE)("span",{key:0,class:(0,a.C4)(e.rateNetClass(n.rate_net))},(0,a.v_)(n.rate_net>0?"+":"")+(0,a.v_)(n.rate_net)+"% ",3)):((0,a.uX)(),(0,a.CE)("span",Et,"—"))])])})),128)),0===e.summary.length?((0,a.uX)(),(0,a.CE)("tr",St,[(0,a.Lk)("td",Bt,(0,a.v_)(e.t("table.noData")),1)])):(0,a.Q3)("",!0)])])]),(0,a.Lk)("h4",Tt,[(0,a.Lk)("span",null,(0,a.v_)(e.t("details.title")),1),(0,a.Lk)("div",Dt,[(0,a.Lk)("button",{type:"button",class:(0,a.C4)(["btn btn-sm","table"===e.view?"btn-primary":"btn-outline-primary"]),onClick:n[23]||(n[23]=function(n){return e.view="table"})},(0,a.v_)(e.t("details.table")),3),(0,a.Lk)("button",{type:"button",class:(0,a.C4)(["btn btn-sm","chart"===e.view?"btn-primary":"btn-outline-primary"]),onClick:n[24]||(n[24]=function(n){return e.view="chart"})},(0,a.v_)(e.t("details.chart")),3)])]),(0,a.Lk)("p",It,[(0,a.Lk)("span",{innerHTML:e.t("details.hint")},null,8,Mt),Ot,(0,a.Lk)("span",{innerHTML:e.t("details.hintNote")},null,8,Ft)]),"table"===e.view?((0,a.uX)(),(0,a.CE)("div",{key:3,ref:"heatmapTopScroll",class:"heatmap-top-scroll",onScroll:n[25]||(n[25]=function(n){return e.syncHeatmapScroll("top")})},[(0,a.Lk)("div",{style:(0,a.Tr)({width:e.heatmapWidth+"px",height:"1px"})},null,4)],544)):(0,a.Q3)("",!0),"table"===e.view?((0,a.uX)(),(0,a.CE)("div",{key:4,ref:"heatmapTableScroll",class:"table-responsive",onScroll:n[26]||(n[26]=function(n){return e.syncHeatmapScroll("bottom")})},[(0,a.Lk)("table",Wt,[(0,a.Lk)("thead",null,[(0,a.Lk)("tr",null,[(0,a.Lk)("th",null,(0,a.v_)(e.t("heatmap.day")),1),((0,a.uX)(),(0,a.CE)(a.FK,null,(0,a.pI)(24,(function(e){return(0,a.Lk)("th",{key:e,class:"text-center"},(0,a.v_)(e-1),1)})),64)),(0,a.Lk)("th",Ht,(0,a.v_)(e.t("heatmap.sumShort")),1)])]),(0,a.Lk)("tbody",null,[((0,a.uX)(!0),(0,a.CE)(a.FK,null,(0,a.pI)(e.heatmapRows,(function(n){return(0,a.uX)(),(0,a.CE)(a.FK,{key:n.key},["month"===n.type?((0,a.uX)(),(0,a.CE)("tr",Rt,[(0,a.Lk)("td",jt,[(0,a.Lk)("b",null,(0,a.v_)(n.label),1),(0,a.Lk)("small",Xt,(0,a.v_)(n.daysCount)+" "+(0,a.v_)(e.pluralDays(n.daysCount)),1)])])):((0,a.uX)(),(0,a.CE)("tr",Nt,[(0,a.Lk)("td",null,[(0,a.Lk)("small",null,(0,a.v_)(n.day),1)]),((0,a.uX)(),(0,a.CE)(a.FK,null,(0,a.pI)(24,(function(r){return(0,a.Lk)("td",{key:r,class:"text-center",style:(0,a.Tr)(e.cellStyle(e.cellAt(n.day,r-1).answers)),title:e.cellTitle(n.day,r-1)},[e.cellAt(n.day,r-1).answers||e.cellAt(n.day,r-1).threads||e.cellAt(n.day,r-1).closed?((0,a.uX)(),(0,a.CE)("span",$t,[(0,a.Lk)("b",Pt,(0,a.v_)(e.cellAt(n.day,r-1).answers),1),Qt,(0,a.Lk)("b",zt,(0,a.v_)(e.cellAt(n.day,r-1).threads),1
2),Kt,(0,a.Lk)("b",Ut,(0,a.v_)(e.cellAt(n.day,r-1).closed),1)])):((0,a.uX)(),(0,a.CE)("span",Yt,"·"))],12,Gt)})),64)),(0,a.Lk)("td",qt,[(0,a.Lk)("span",Vt,[(0,a.Lk)("b",Zt,(0,a.v_)(e.daySum(n.day).answers),1),Jt,(0,a.Lk)("b",te,(0,a.v_)(e.daySum(n.day).threads),1),ee,(0,a.Lk)("b",ne,(0,a.v_)(e.daySum(n.day).closed),1)])])]))],64)})),128)),0===e.heatmapDays.length?((0,a.uX)(),(0,a.CE)("tr",re,[(0,a.Lk)("td",ae,(0,a.v_)(e.t("heatmap.noData")),1)])):(0,a.Q3)("",!0)])],512)],544)):((0,a.uX)(),(0,a.CE)("div",se,[(0,a.Lk)("canvas",oe,null,512)]))])}],["__scopeId","data-v-22404036"]])},74785:(e,n,r)=>{var a=r(50382);a.__esModule&&(a=a.default),"string"==typeof a&&(a=[[e.id,a,""]]),a.locals&&(e.exports=a.locals);(0,r(70534).A)("43c2bdac",a,!0,{})}}]);
3//# sourceMappingURL=1966.6ac9f24fd1b2ceac03d6.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.