Разработка пользовательского интерфейса

Мне было поручено изменить дизайн представления записей домохозяйств для MortarStone. MortarStone — это платформа SaaS, которая позволяет некоммерческим организациям и церквям оценивать и управлять своими пожертвованиями, а просмотр записей домохозяйств — это место, где пользователи проверяют отдельных доноров. Неудивительно, что большая часть этого представления заключалась в том, чтобы показать, сколько пожертвовало каждое домохозяйство, когда они пожертвовали и какому фонду принадлежит пожертвование.

Предыдущая версия MortarStone показывала эту информацию в двух разных таблицах, но было много недостатков в таком отображении. Было легко посмотреть на отдельные случаи пожертвований, но табличный формат затруднял измерение роста с течением времени или сравнение сумм пожертвований по фондам. Это было проблемой, потому что одно из основных преимуществ нашего продукта заключается в том, что мы позволяем пользователям легко определять тенденции подарков и не только просматривать отдельные подарки, как вы можете в электронной таблице, но и четко показывать вам общую картину.

Я начал с каркасного решения. После нескольких итераций мы пришли к этому макету, который показывает наиболее важные общие данные о домохозяйстве вверху и некоторые статистические данные внизу. В середине мы хотели включить диаграмму пожертвований для проверки пожертвований как на макро-, так и на микроуровне. Я намеренно не тратил время на то, чтобы слишком много думать о том, как будет выглядеть или вести себя диаграмма, потому что я не хотел продавать идею дизайна нашим заинтересованным сторонам, а затем обнаруживать, что реализовать ее технически невозможно. Итак, на этапе проектирования я изложил основные элементы и доверил разработчику найти такое решение для создания диаграмм, которое было бы относительно простым в реализации, достаточно гибким, чтобы его можно было настроить в соответствии с нашими желаниями, и масштабируемым.

Разработан каркас без концепции диаграммы

Поиск подходящей библиотеки диаграмм

Раньше я создавал диаграммы с несколькими популярными библиотеками javascript, но то, что я пробовал раньше, меня не совсем устраивало ни с функциональной, ни с эстетической точки зрения — я искал новую библиотеку, и начались поиски.

После некоторого поиска хорошей библиотеки диаграмм я решил остановиться на замечательном C3.js. C3.js — это простая, чистая библиотека javascript, зависящая только от D3.js. У меня был некоторый опыт использования D3.js для создания пользовательских визуализаций с нуля, и каким бы замечательным ни был D3.js, я подумал, что создавать свои собственные диаграммы с нуля было бы излишним. C3.js был отличным решением, потому что он предлагает надежную коллекцию предварительно настроенных типов диаграмм и удобный API для выполнения некоторых общих настроек, но он просто использует D3.js под капотом, поэтому вы по-прежнему можете делать все что угодно. вы хотели бы напрямую через D3. Графики также были очень минималистичными, включали в себя сделанные со вкусом анимации из коробки и в целом выглядели великолепно!

Реализация диаграмм

Настроить графики не составило труда. Я установил и C3, и D3 с помощью Bower и был готов начать помещать наши данные в простые для понимания диаграммы. Я создал службу AngularJS под названием msCharts, которая обрабатывает все общие настройки диаграммы. Это включает в себя обеспечение видимости условных обозначений и меток диаграммы на мобильных устройствах, создание красивых описательных всплывающих подсказок и, по сути, получение данных из обычного массива и передачу их в C3.js в правильном синтаксисе.

Служба msCharts

'use strict';

angular.module('frontendCompanyApp')
.service('msCharts', function ($window) {
  var self = this;
  // rotate and cull legend on small layouts
  var rotateDegrees = 0;
  var culling = false;
  var clientWidth = Math.max(document.documentElement.clientWidth, $window.innerWidth || 0);
  if (clientWidth < 960) {
    rotateDegrees = 60;
    culling = true;
  }
  this.create = function (id, format, xAxisLabels, segments) {
    function getFormat () {
      var formatSet;
      if (format === undefined) {
        formatSet = d3.format(',');
      } else if (format === 'days') {
        formatSet = function (data) { return data.toFixed() + ' days'; };
      } else if (format === 'dollars') {
        formatSet = d3.format('$,.0f');
      } else {
        formatSet = d3.format(format);
      }
      return formatSet;
    }
    var chart = c3.generate({
      bindto: '#givingChart',
      data: {
        columns: [],
        groups: [segments],
        order: 'desc',
      },
      legend: {
        item: {
          onclick: function (id) { 
            chart.toggle(id);
          }
        }
      },
      axis: {
        x: {
          type: 'category',
          categories: xAxisLabels,
          tick: {
            culling: culling,
            rotate: rotateDegrees
          }
        },
        y: {
          tick: {
            format: getFormat()
          }
        }
      },
      grid: {
        focus: {
          show: false
        }
      },
      tooltip: {
        grouped: true,
        format: getFormat(),
        contents: function (data, defaultTitleFormat, defaultValueFormat, color) {
          var template = self.givingTooltipTemplate(data, xAxisLabels, color);
          return template;
        }
      }
    });
    return chart;
  };
  this.givingTooltipTemplate = function (data, xAxisLabels, color) {
    var total = 0;
    data.forEach(function (dataPoint) {total += dataPoint.value;});
    var template = '' +
      '<md-card class="chart__tooltip">' +
        '<h3 class="chart__tooltip__header" layout="row" layout-align="start">' +
          '<div flex>' + xAxisLabels[data[0].index] + ' total:</div>' +
          '<div flex class="align-right"> $' + total.toLocaleString() + '</div>' +
        '</h3>' +
        '<div class="chart__tooltip__body" layout="column">';
    data.forEach(function (dataPoint) {
      template += '' + 
        '<div layout="row" layout-align="space-between start">' +
          '<div flex>' + '<div class="chart__tooltip__swatch" style="background-color: '+ color(dataPoint.id) + '"></div>' + dataPoint.name + '</div>' +
          '<div flex class="align-right"> $' + dataPoint.value.toLocaleString() + '</div>' +
        '</div>';
    });
    template += '</div></md-card>';
    return template;
  };
});

После того, как это было настроено, все, что мне нужно было сделать, это получить данные с сервера и вызвать мой удобный сервисный метод msCharts.create().

Создание данных из данных сервера

var app = angular.module('frontendCompanyApp');
app.controller('HouseholdRecordCtrl', function (
  $scope, $http, msCharts, $cookies
) { 
    var self = this; 
    this.getGiftsGroup = function (range) {
    // Save range preference via cookies
    $cookies.put('givingRange', range.key);     
    self.givingChart.loading = true;
    self.givingChart.error = false;
    self.givingRange = range.label;
    $http.get('/api/v1/donors_service/donor_unit/donations/grouped', { params: { donor_unit_id: donorUnitId, type: range.key } })
    .then(function (response) {
      self.givingChart.loading = false;
      createGiftsChart(response.data.grouped);
    }, function (error) {
      self.givingChart.loading = false;
      self.givingChart.error = true;
      console.log('Gifts grouped request error:', error);
    });
  };
)};

Окончательный результат

Я выбрал диаграмму с областями с накоплением. Этот тип диаграммы представляет количество по оси Y по размеру области и позволяет увидеть, как различные типы данных составляют общую сумму. Это было отличное решение, потому что оно позволяет нашим пользователям просматривать пожертвования в целом, а также видеть, из каких средств состоит пожертвование. Наши пользователи были очень довольны тем, как получились диаграммы. Теперь они могут…

  • Просматривайте пожертвования домохозяйства во времени
  • Выберите для просмотра пожертвований за последние 12 месяцев, текущий год, месяц или квартал.
  • Выберите, чтобы показать или скрыть отдельные средства
  • Наведите указатель мыши на точки данных, чтобы открыть подробности
  • Просмотр их графиков на мобильных устройствах