diff --git a/CHANGELOG.md b/CHANGELOG.md index da12aa7..f1af096 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Улучшения +- **График открытых заявок** — переключатель **«По неделям»** / **«По дням»**: 16 недель (как раньше) или 112 дней с тем же смыслом точки (снимок «сколько было открыто» на момент); боковая панель сравнения в режиме дней подписывает вторую строку «день назад». - **Тепловая карта по месяцам** переработана: вместо сетки месяц×день-недели теперь отображается одна строка из 12 ячеек (по одной на месяц), каждая окрашена по числу заявок. Значение показывается при наведении. - **Блок сравнения** («сейчас / неделю назад / начало месяца») теперь растягивается в высоту графика (`align-items: stretch`), элементы равномерно распределяются по высоте — цифры больше не выступают за пределы графика. - **Повторные клиенты** — один счётчик «открытых» через `isTaskOpen` (в т.ч. «в работе», «новая», «назначена» и т.п.); одна акцентная полоса и бейдж `N откр.` без разделения цветов. @@ -19,7 +20,7 @@ | Файл | Изменения | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `public/js/statistics.js` | `MONTH_COUNTS` вместо `MONTH_HM`; новый HTML месячной тепловой карты; `renderClientsList` с единой логикой `open` и правкой `opPct` для `clientsOnlyOpen` | +| `public/js/statistics.js` | `MONTH_COUNTS` вместо `MONTH_HM`; новый HTML месячной тепловой карты; `renderClientsList` с единой логикой `open` и правкой `opPct` для `clientsOnlyOpen`; переключатель недели/дни для графика открытых (`openChartByDays`, `lineOpen` / `lineSlot`) | | `public/css/app.css` | `.stats-month-hm`*; `align-items: stretch` в `.stats-open-wrap`; уменьшен `.soc-val`; стили бейджа клиентов без раздельных полос | diff --git a/public/js/statistics.js b/public/js/statistics.js index 2ac595f..756ebf0 100644 --- a/public/js/statistics.js +++ b/public/js/statistics.js @@ -6,6 +6,8 @@ let statsPageOpen = false; let clientsExpanded = false; let clientsOnlyOpen = false; let timingPeriodIdx = 4; +/** false = по неделям (16 точек), true = по дням (112 дней) — график «открытых заявок» */ +let openChartByDays = false; const STATS_CACHE_KEY = 'intradesk_stats_html_v2'; function saveStatsCache() { @@ -93,9 +95,40 @@ function renderStatsPage() { wCreated.push(data.filter(({task:t})=>{const c=new Date(t.createdat);return c>=wStart&&c{const c=new Date(t.resolutiondatefact||t.closedat||0);return c>=wStart&&c= 0; i--) { + const tEnd = new Date(now - i * 86400000); + lineLabels.push(tEnd.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' })); + lineOpen.push(data.filter(({ task: t }) => { + const cr = new Date(t.createdat); + if (cr >= tEnd) return false; + if (!isTaskDone({ task: t })) return true; + return new Date(t.resolutiondatefact || t.closedat || 0) > tEnd; + }).length); + } + maxOpen = Math.max(...lineOpen, 1); + } else { + lineLabels = wLabels; + lineOpen = wOpen; + lineN = WEEKS; + lineSlot = 60; + lineDotR = 5; + lineLabelStep = 2; + maxOpen = maxOpenWeekly; + } + // Открытых на начало текущего месяца const firstDayOfMonth = new Date(now); firstDayOfMonth.setDate(1); firstDayOfMonth.setHours(0,0,0,0); const openAtMonthStart = data.filter(({task:t})=>{ const cr=new Date(t.createdat); if(cr>=firstDayOfMonth)return false; if(!isTaskDone({task:t}))return true; return new Date(t.resolutiondatefact||t.closedat||0)>firstDayOfMonth; }).length; @@ -368,36 +401,42 @@ function renderStatsPage() { - +
-
📈 Открытых заявок на конец недели
+
+ 📈 Открытых заявок ${openChartByDays ? 'по дням' : 'на конец недели'} +
+ + +
+
- + - ${(()=>{ if(maxOpen===0)return''; const pts=wOpen.map((v,i)=>[i*60+30,115-v/maxOpen*95]); const area='M'+pts[0]+' '+pts.slice(1).map(p=>'L'+p).join(' ')+' L'+pts[pts.length-1][0]+',115 L'+pts[0][0]+',115 Z'; const line='M'+pts[0]+' '+pts.slice(1).map(p=>'L'+p).join(' '); return''+pts.map(([x,y],i)=>'').join(''); })()} + ${(()=>{ if(maxOpen===0)return''; const cx=i=>i*lineSlot+lineSlot/2; const pts=lineOpen.map((v,i)=>[cx(i),115-v/maxOpen*95]); const area='M'+pts[0]+' '+pts.slice(1).map(p=>'L'+p).join(' ')+' L'+pts[pts.length-1][0]+',115 L'+pts[0][0]+',115 Z'; const line='M'+pts[0]+' '+pts.slice(1).map(p=>'L'+p).join(' '); return''+pts.map(([x,y],i)=>'').join(''); })()} -
${wLabels.map((l,i)=>i%2===0?`${l}`:'').join('')}
+
${lineLabels.map((l,i)=>i%lineLabelStep===0?`${l}`:'').join('')}
-
${wOpen[WEEKS-1]}
+
${lineOpen[lineN-1]}
сейчас
-
${wOpen[WEEKS-2]}
-
неделю назад
- ${wDiffBadge(wOpen[WEEKS-1]-wOpen[WEEKS-2])} +
${lineN>=2?lineOpen[lineN-2]:'—'}
+
${openChartByDays?'день назад':'неделю назад'}
+ ${lineN>=2?wDiffBadge(lineOpen[lineN-1]-lineOpen[lineN-2]):''}
${openAtMonthStart}
нач. месяца
- ${wDiffBadge(wOpen[WEEKS-1]-openAtMonthStart)} + ${wDiffBadge(lineOpen[lineN-1]-openAtMonthStart)}