From e414524eab6bfc3f06ed3cd705d67f01c9880d27 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 19 Aug 2021 16:17:59 +0530 Subject: [PATCH 1/3] refactor form results display * improve table component reusability for the rest of the platform * display count of empty results at the top of each section * remove unused styles * fix incorrect methods for counting empty answers for multi-checkbox questions --- customize.dist/src/less2/include/charts.less | 52 ++++++++ www/form/app-form.less | 64 +--------- www/form/inner.js | 120 ++++++++++--------- 3 files changed, 117 insertions(+), 119 deletions(-) diff --git a/customize.dist/src/less2/include/charts.less b/customize.dist/src/less2/include/charts.less index c8b983707..ea402094f 100644 --- a/customize.dist/src/less2/include/charts.less +++ b/customize.dist/src/less2/include/charts.less @@ -50,4 +50,56 @@ } } } + + .cp-charts-cell { + border: 1px solid @cp_form-border; + display: table-cell; + padding: 5px 10px; + background: @cp_form-bg2; + } + + .cp-form-results-type-radio { + .cp-form-results-type-multiradio-data { + display: flex; + flex-flow: column; + } + .cp-form-results-type-radio-data { + display: table-row; + border: 1px solid @cp_form-border; + & > span { + .cp-charts-cell(); + } + } + } + + .cp-charts.cp-bar-table, .cp-charts.cp-text-table { + display: table; + width: 100%; + .cp-charts-row { + display: table-row; + border: 1px solid @cp_form-border; + &.full { + display: flex; + flex-flow: column; + } + + & > span { + .cp-charts-cell(); + display: table-cell; + &.cp-value { + min-width: 200px; + } + &.cp-bar-container { + width: 99%; + padding: 0px; + position: relative; + .cp-bar { + position: absolute; + background: @cryptpad_color_brand; + height: 100%; + } + } + } + } + } } diff --git a/www/form/app-form.less b/www/form/app-form.less index 53acb92af..5ef211201 100644 --- a/www/form/app-form.less +++ b/www/form/app-form.less @@ -519,69 +519,9 @@ i { margin-right: 5px; } background: fade(@cryptpad_text_col, 15%); } - .cp-form-results-type-text { - max-height: 300px; + .cp-form-results-contained { + max-height: 350px; // enough for 10 table entries overflow: auto; - .cp-form-results-type-text-data { - padding: 5px 10px; - background: @cp_form-bg2; - &:not(:last-child) { margin-bottom: 1px; } - } - } - .cp-form-results-type-textarea-data { - white-space: pre-wrap; - padding: 5px 10px; - background: @cp_form-bg2; - &:not(:last-child) { margin-bottom: 1px; } - } - - .cp-form-results-cell() { - border: 1px solid @cp_form-border; - display: table-cell; - padding: 5px 10px; - background: @cp_form-bg2; - } - - .cp-form-results-type-multiradio-data { - .cp-mr-q { - font-weight: bold; - padding: 5px 10px; - .cp-form-results-cell(); - } - &:not(:first-child) { - .cp-mr-q { - margin-top: 15px; - } - } - } - - .cp-form-results-type-radio { - display: table; - width: 100%; - .cp-form-results-type-multiradio-data { - display: flex; - flex-flow: column; - } - .cp-form-results-type-radio-data { - display: table-row; - border: 1px solid @cp_form-border; - & > span { - .cp-form-results-cell(); - &.cp-value { - min-width: 200px; - } - &.cp-bar-container { - width: 99%; - padding: 0px; - position: relative; - .cp-bar { - position: absolute; - background: @cryptpad_color_brand; - height: 100%; - } - } - } - } } .cp-form-individual { background: @cp_form-bg1; diff --git a/www/form/inner.js b/www/form/inner.js index ec329830f..cb85ae29c 100644 --- a/www/form/inner.js +++ b/www/form/inner.js @@ -803,10 +803,31 @@ define([ return total; }; - var getEmpty = function (empty) { // TODO don't include this in the scrollable area - if (empty) { - return UI.setHTML(h('div.cp-form-results-type-text-empty'), Messages._getKey('form_notAnswered', [empty])); - } + var multiAnswerSubHeading = function (content) { + return h('span.cp-charts-row', h('td.cp-charts-cell', { + colspan: 3, + style: 'font-weight: bold;', + }, content)); + }; + + var getEmpty = function (empty) { + if (!empty) { return; } + var msg = UI.setHTML(h('span.cp-form-results-empty-text'), Messages._getKey('form_notAnswered', [empty])); + return multiAnswerSubHeading(msg); + }; + + var barGraphic = function (itemScale) { + return h('span.cp-bar-container', h('div.cp-bar', { + style: 'width: ' + (itemScale * 100) + '%', + }, ' ')); + }; + + var barRow = function (value, count, max, showBar) { + return h('div.cp-charts-row', [ + h('span.cp-value', value), + h('span.cp-count', count), + showBar? barGraphic((count / max)): undefined, + ]); }; var findItem = function (items, uid) { @@ -957,27 +978,21 @@ define([ return Array.isArray(A)? Math.max.apply(null, A): NaN; }; - var barGraphic = function (itemScale) { - return h('span.cp-bar-container', h('div.cp-bar', { - style: 'width: ' + (itemScale * 100) + '%', - }, ' ')); - }; - var renderTally = function (tally, empty, showBar) { var rows = []; var counts = Util.values(tally); var max = arrayMax(counts); + if (empty) { rows.push(getEmpty(empty)); } Object.keys(tally).forEach(function (value) { var itemCount = tally[value]; var itemScale = (itemCount / max); - rows.push(h('div.cp-form-results-type-radio-data', [ + rows.push(h('div.cp-charts-row', [ h('span.cp-value', value), h('span.cp-count', itemCount), showBar? barGraphic(itemScale): undefined, ])); }); - if (empty) { rows.push(getEmpty(empty)); } return rows; }; @@ -1015,34 +1030,35 @@ define([ reset: function () { $tag.val(''); } }; }, - printResults: function (answers, uid) { + printResults: function (answers, uid) { // results text var results = []; var empty = 0; var tally = {}; + var isEmpty = function (answer) { + console.error("EMPTY?", JSON.stringify(answer)); + return !answer || !answer.trim(); + }; + Object.keys(answers).forEach(function (author) { var obj = answers[author]; var answer = obj.msg[uid]; - if (!answer || !answer.trim()) { return empty++; } + if (isEmpty(answer)) { return empty++; } Util.inc(tally, answer); }); //var counts = Util.values(tally); //var max = arrayMax(counts); //if (max < 2) { // there are no duplicates, so just return text + results.push(getEmpty(empty)); Object.keys(answers).forEach(function (author) { var obj = answers[author]; var answer = obj.msg[uid]; if (!answer || !answer.trim()) { return empty++; } - results.push(h('div.cp-form-results-type-text-data', answer)); + results.push(h('div.cp-charts-row', h('span.cp-value', answer))); }); - results.push(getEmpty(empty)); - return h('div.cp-form-results-type-text', results); + return h('div.cp-form-results-contained', h('div.cp-charts.cp-text-table', results)); //} -/* - var rendered = renderTally(tally, empty); - return h('div.cp-form-results-type-text', rendered); -*/ }, icon: h('i.cptools.cptools-form-text') }, @@ -1104,11 +1120,11 @@ define([ var obj = answers[author]; var answer = obj.msg[uid]; if (!answer || !answer.trim()) { return empty++; } - results.push(h('div.cp-form-results-type-textarea-data', answer)); + results.push(h('div.cp-charts-row', h('span.cp-value', answer))); }); - results.push(getEmpty(empty)); + results.unshift(getEmpty(empty)); - return h('div.cp-form-results-type-text', results); + return h('div.cp-form-results-contained', h('div.cp-charts.cp-text-table', results)); }, icon: h('i.cptools.cptools-form-paragraph') }, @@ -1179,7 +1195,7 @@ define([ }); var rendered = renderTally(count, empty, showBars); - return h('div.cp-form-results-type-radio', rendered); + return h('div.cp-charts.cp-bar-table', rendered); }, icon: h('i.cptools.cptools-form-list-radio') }, @@ -1285,26 +1301,17 @@ define([ max = arrayMax(counts); }); + results.push(getEmpty(empty)); count_keys.forEach(function (q_uid) { var q = findItem(opts.items, q_uid); var c = count[q_uid]; - - var values = Object.keys(c).map(function (res) { - var itemCount = c[res]; - return h('div.cp-form-results-type-radio-data', [ - h('span.cp-value', res), - h('span.cp-count', itemCount), - showBars? barGraphic((itemCount / max)): undefined, - ]); + results.push(multiAnswerSubHeading(q)); + Object.keys(c).forEach(function (res) { + results.push(barRow(res, c[res], max, showBars)); }); - results.push(h('div.cp-form-results-type-multiradio-data', [ - h('span.cp-mr-q', q), - h('span.cp-mr-value', values) - ])); }); - results.push(getEmpty(empty)); - return h('div.cp-form-results-type-radio', results); + return h('div.cp-charts.cp-bar-table', results); }, exportCSV: function (answer, form) { var opts = form.opts || {}; @@ -1401,7 +1408,7 @@ define([ }); var rendered = renderTally(count, empty, showBars); - return h('div.cp-form-results-type-radio', rendered); + return h('div.cp-charts.cp-bar-table', rendered); }, icon: h('i.cptools.cptools-form-list-check') }, @@ -1498,10 +1505,19 @@ define([ var empty = 0; var count = {}; var showBars = Boolean(content); + + var isEmpty = function (answer) { + if (!answer) { return true; } + return !Object.keys(answer).some(function (k) { + var A = answer[k]; + return Array.isArray(A) && A.length; + }); + }; + Object.keys(answers).forEach(function (author) { var obj = answers[author]; var answer = obj.msg[uid]; - if (!answer || !Object.keys(answer).length) { return empty++; } + if (isEmpty(answer)) { return empty++; } Object.keys(answer).forEach(function (q_uid) { var c = count[q_uid] = count[q_uid] || {}; var res = answer[q_uid]; @@ -1520,26 +1536,17 @@ define([ max = arrayMax(counts); }); + results.push(getEmpty(empty)); count_keys.forEach(function (q_uid) { var q = findItem(opts.items, q_uid); var c = count[q_uid]; - - var values = Object.keys(c).map(function (res) { - var val = c[res]; - return h('div.cp-form-results-type-radio-data', [ - h('span.cp-value', res), - h('span.cp-count', val), - showBars? barGraphic(val / max) : undefined, - ]); + results.push(multiAnswerSubHeading(q)); + Object.keys(c).forEach(function (res) { + results.push(barRow(res, c[res], max, showBars)); }); - results.push(h('div.cp-form-results-type-multiradio-data', [ - h('span.cp-mr-q', q), - h('span.cp-mr-value', values), - ])); }); - results.push(getEmpty(empty)); - return h('div.cp-form-results-type-radio', results); + return h('div.cp-charts.cp-bar-table', results); }, exportCSV: function (answer, form) { var opts = form.opts || {}; @@ -1658,7 +1665,6 @@ define([ // results sort var opts = form[uid].opts || TYPES.sort.defaultOpts; var l = (opts.values || []).length; - //var results = []; var empty = 0; var count = {}; var showBars = Boolean(content); @@ -1673,7 +1679,7 @@ define([ }); var rendered = renderTally(count, empty, showBars); - return h('div.cp-form-results-type-radio', rendered); + return h('div.cp-charts.cp-bar-table', rendered); }, icon: h('i.cptools.cptools-form-list-ordered') }, From bfdcf4ec0ce38f9e2930a8d988cc038a4f6f4df9 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 19 Aug 2021 18:20:14 +0530 Subject: [PATCH 2/3] fix user/display name rendering which I accidentally broke in the user admin menu --- www/common/toolbar.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 8bd0fcc32..d103fe410 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -53,6 +53,7 @@ MessengerUI, Messages, Pages) { var USERADMIN_CLS = Bar.constants.user = 'cp-toolbar-user-dropdown'; var USERNAME_CLS = Bar.constants.username = 'cp-toolbar-user-name'; /*var READONLY_CLS = */Bar.constants.readonly = 'cp-toolbar-readonly'; + var USERBUTTON_CLS = Bar.constants.changeUsername = "cp-toolbar-user-rename"; // Create the toolbar element @@ -1028,6 +1029,12 @@ MessengerUI, Messages, Pages) { var userMenuCfg = { $initBlock: $userAdmin, }; + if (!config.hideDisplayName) { + $.extend(true, userMenuCfg, { + displayNameCls: USERNAME_CLS, + changeNameButtonCls: USERBUTTON_CLS, + }); + } if (config.readOnly !== 1) { userMenuCfg.displayName = 1; userMenuCfg.displayChangeName = 1; From eafe27ffb46a00fa16e4ea9369c0b51e58b30dee Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 19 Aug 2021 18:21:48 +0530 Subject: [PATCH 3/3] reuse cp-charts to visualize server task running time as bar charts --- www/admin/app-admin.less | 24 +++++++++++++++++++ www/admin/inner.js | 52 +++++++++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index 227792540..f66eb75f7 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -1,12 +1,14 @@ @import (reference) '../../customize/src/less2/include/framework.less'; @import (reference) '../../customize/src/less2/include/sidebar-layout.less'; @import (reference) '../../customize/src/less2/include/support.less'; +@import (reference) '../../customize/src/less2/include/charts.less'; &.cp-app-admin { .framework_min_main(); .sidebar-layout_main(); .support_main(); + .charts_main(); .cp-hidden { display: none !important; @@ -294,5 +296,27 @@ } } } + span.cp-bar.profiling-percentage { + text-align: center; + padding: 5px; + } + span.profiling-label { + position: absolute; + z-index: 1; + width: 100%; + text-align: center; + padding: 5px; + } + #profiling-chart { + .cp-bar-container { + max-width: 400px; + } + } + .width-constrained { + max-width: 800px; + } + .cp-charts-row.heading { + font-weight: bold; + } } diff --git a/www/admin/inner.js b/www/admin/inner.js index e8d567d41..63c516614 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -1673,34 +1673,51 @@ define([ var $div = makeBlock('performance-profiling'); // Msg.admin_performanceProfilingHint, .admin_performanceProfilingTitle var onRefresh = function () { - var body = h('tbody'); + var createBody = function () { + return h('div#profiling-chart.cp-charts.cp-bar-table', [ + h('span.cp-charts-row.heading', [ + h('span', Messages.admin_performanceKeyHeading), + h('span', Messages.admin_performanceTimeHeading), + h('span', Messages.admin_performancePercentHeading), + //h('span', ''), //Messages.admin_performancePercentHeading), + ]), + ]); + }; - var table = h('table#cp-performance-table', [ - h('thead', [ - h('th', Messages.admin_performanceKeyHeading), - h('th', Messages.admin_performanceTimeHeading), - h('th', Messages.admin_performancePercentHeading), - ]), - body, - ]); - var appendRow = function (key, time, percent) { - console.log("[%s] %ss running time (%s%)", key, time, percent); - body.appendChild(h('tr', [ key, time, percent ].map(function (x) { - return h('td', x); - }))); + var body = createBody(); + var appendRow = function (key, time, percent, scaled) { + //console.log("[%s] %ss running time (%s%)", key, time, percent); + body.appendChild(h('span.cp-charts-row', [ + h('span', key), + h('span', time), + //h('span', percent), + h('span.cp-bar-container', [ + h('span.cp-bar.profiling-percentage', { + style: 'width: ' + scaled + '%', + }, ' ' ), + h('span.profiling-label', percent + '%'), + ]), + ])); }; var process = function (_o) { + $('#profiling-chart').remove(); + body = createBody(); var o = _o[0]; var sorted = Object.keys(o).sort(function (a, b) { if (o[b] - o[a] <= 0) { return -1; } return 1; }); + + var values = sorted.map(function (k) { return o[k]; }); var total = 0; - sorted.forEach(function (k) { total += o[k]; }); + values.forEach(function (value) { total += value; }); + var max = Math.max.apply(null, values); + sorted.forEach(function (k) { var percent = Math.floor((o[k] / total) * 1000) / 10; - appendRow(k, o[k], percent); + appendRow(k, o[k], percent, (o[k] / max) * 100); }); + $div.append(h('div.width-constrained', body)); }; sFrameChan.query('Q_ADMIN_RPC', { @@ -1710,10 +1727,7 @@ define([ UI.warn(Messages.error); return void console.error(e, data); } - //console.info(data); - $div.find("table").remove(); process(data); - $div.append(table); }); };