Как сделать Task manager на основе гугл таблиц и чекбоксов

Недавно в комментариях «прилетел» следующий вопрос:

Здравствуйте!

А как настроить на флажки проверку данных ?

К примеру есть массив данных, скажем сверху я создаю два чекбокса «в работе» и «завершено» и при нажатии на любой мне соответственно отображаются только нужные значения (статус «в работе»).

Знаю как это реализовать через функцию FILTER , но для этого придется создавать вторую таблицу, что неудобно.

и хотя уточняющего ответа не было, задача показалась интересной и я решил попробовать ее реализовать.

Часть 1: Табличная

Тут все не сложно:

Создаю два чекбокса и подписываю, что они должны характеризовать по задаче:

варианты чекбоксов

Ниже делаю условную таблицу с задачами и их статусом:

task list

Выпадающий список сделан на основе данных ячеек B1 и B2:

проверка данных

На этом собственно всё. Табличную часть можно считать законченной. Выглядит она следующим образом:

google sheet task manager

Часть 2: Скрипты

Перехожу в скрипты:

Apps script

И первое, что делаю, обращаюсь к нужному листу книги и возвращаю массив со статусом задач:

const ss = SpreadsheetApp.getActiveSpreadsheet();
const wsStatus = ss.getSheetByName('Task');

function getAllStatus() {
  const textToFind = "Статус"
  const startRow = wsStatus.createTextFinder(textToFind).findNext().getRowIndex();
  const endPosition = wsStatus.getLastRow();

  let allStatus = [];
  for (let i = startRow + 1; i <= endPosition; i++) {
    allStatus.push([wsStatus.getRange('A' + i).getRowIndex(), wsStatus.getRange('A' + i).getValue()]);
  }
  return allStatus;
}

Выглядит вернувшийся массив так:

где первый элемент подмассива это номер строки, второй — статус.

Зачем нужно вот это:

const textToFind = "Статус"
  const startRow = wsStatus.createTextFinder(textToFind).findNext().getRowIndex();

Скрипт находит текст «статус» и возвращает индекс его ряда: в данном примере это 4.

Я решил отказаться от фиксации первой строки, и того что будет забираться в массив по следующей причине: если добавить строки между чекбоксами и таблицей ниже, то будет ошибка.

for (let i = startRow + 1; i <= endPosition; i++) {
    allStatus.push([wsStatus.getRange('A' + i).getRowIndex(), wsStatus.getRange('A' + i).getValue()]);

В итоге от строчки после шапки «Статус» идет сбор массива.

Следующий шаг это раскрыть весь список, независимо от того, что могло быть ранее скрыто:

function unhideAll() {
  for (let elem of getAllStatus()) {
    wsStatus.unhideRow(wsStatus.getRange(`A${elem[0]}`))
  }
}

то есть каждая строка (а именно с 5 по 13 включительно) указанная в getAllStatus() будет раскрыта.

Следующая функция получает состояние нажатых чекбоксов:

function checkBoxResults() {
  if (wsStatus.getRange('A1').getValue() && wsStatus.getRange('A2').getValue()) {
    return 2;
  } else if (wsStatus.getRange('A1').getValue()) {
    return 1;
  } else if (wsStatus.getRange('A2').getValue()) {
    return 3;
  } else {
    return 0;
  }
}

wsStatus.getRange('A1').getValue() && wsStatus.getRange('A2').getValue() — если нажаты оба, вернуть цифру 2

wsStatus.getRange('A1').getValue() — если только первый, возвращается 1

wsStatus.getRange('A2').getValue() — если только второй, возвращается 3

Во всех остальных случаях возвращается 0.

Ну и последняя функция — для отображения нужных строк:

function filterStatus() {
  const allStatusRow = getAllStatus()
  const checkBoxSum = checkBoxResults()

  let filteredArr = []

  switch (checkBoxSum) {
    case 1:
      filteredArr = allStatusRow.filter(row => row[1] !== wsStatus.getRange('B1').getValue())
      unhideAll();
      for (let elem of filteredArr) {
        wsStatus.hideRow(wsStatus.getRange(`A${elem[0]}`))
      }
      break;

    case 2:
      filteredArr = allStatusRow.filter(row => (row[1] !== wsStatus.getRange('B1').getValue() && row[1] !== wsStatus.getRange('B2').getValue()))
      unhideAll();
      for (let elem of filteredArr) {
        wsStatus.hideRow(wsStatus.getRange(`A${elem[0]}`))
      }
      break;

    case 3:
      filteredArr = allStatusRow.filter(row => row[1] !== wsStatus.getRange('B2').getValue())
      unhideAll();
      for (let elem of filteredArr) {

        wsStatus.hideRow(wsStatus.getRange(`A${elem[0]}`))
      }
      break;

    default:
      unhideAll();
      break;
  }
}

const allStatusRow = getAllStatus() — получаю весь массив

const checkBoxSum = checkBoxResults() — получаю значения 0-1-2-3 по нажатым чекбоксам

Далее через switch (case) фильтрую массив на основе выбранных чекбоксов и прячу необходимые строки, например по case 1:

case 1:
      filteredArr = allStatusRow.filter(row => row[1] !== wsStatus.getRange('B1').getValue())
      unhideAll();
      for (let elem of filteredArr) {
        wsStatus.hideRow(wsStatus.getRange(`A${elem[0]}`))
      }
      break;

filteredArr = allStatusRow.filter(row => row[1] !== wsStatus.getRange('B1').getValue()) — массив где указаны строки НЕ равные смыслу нажатого чекбокса.

unhideAll(); — принудительно сначала все раскрываю.

wsStatus.hideRow(wsStatus.getRange(`A${elem[0]}`)) — скрываю строку которая НЕ равна смыслу нажатого чекбокса.

Далее необходимо функцию filterStatus() повесить в onEdit():

function onEdit(){
   filterStatus()
}

Потом сохраниться и обновить таблицу.

Теперь при нажатии на чекбоксы ненужные статусы будут прятаться:

Добавить комментарий