kintoneの一覧画面で予定を検索する

今回は、期間のある予定を検索するアプリを作ってみましょう。

指定の期間に少しでもかすっていたら検索対象にして、
指定の期間外だったら検索対象外にするアプリです。

<iframe width="560" height="315" src="https://www.youtube.com/embed/pb4CY3uOXbc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# アプリの準備

| フィールド種類 | フィールドコード | 備考 |
|:-:|:-:|:-:|
|  文字列(1行) | 社員名  |  ほんとに作るときはルックアップとかがいいかも |
|  日付 |開始日   |   |
|  日付 | 終了日  |   |
| 文字列(1行)  | 内容  |   |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/8c2452df-93ae-49f8-2a8b-b9723f5287b0.png)

# JavaScript

ライブラリ結構使います。

**jQuery**
`https://js.cybozu.com/jquery/3.6.0/jquery.min.js`

**jQueryUI**
js : `https://js.cybozu.com/jqueryui/1.12.1/jquery-ui.min.js`
css: `https://js.cybozu.com/jqueryui/1.12.1/themes/smoothness/jquery-ui.css`

**datepicker日本語化**
`https://rawgit.com/jquery/jquery-ui/master/ui/i18n/datepicker-ja.js`

**kintone UI Component**
`https://unpkg.com/kintone-ui-component/umd/kuc.min.js`

※setIntervalで設置しているけどあんまり真似しちゃダメなやつです

## コード

```js
kintone.events.on(["app.record.index.show"], (event) => {
  // ボタン増殖を防ぐ
  if (document.getElementById("dateS")) {
    return;
  }

  // 検索ボタンからきたとき初期値入れとく
  const queryCondition = kintone.app.getQueryCondition();
  let iniDateS = "";
  let iniDateE = "";
  let inirB = "含む";
  if (queryCondition) {
    const str = queryCondition
      .replaceAll(" ", "")
      .replace(/and|or/, "kugiri")
      .split("kugiri");
    // 日付のみ取り出して初期値に入れる
    const regex = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
    iniDateS = str[0].match(regex)[0];
    iniDateE = str[1].match(regex)[0];

    // ラジオボタン初期値 or のクエリだったら除外
    if (/or/.test(queryCondition)) {
      inirB = "除外";
    }
  }

  // 日付、検索ボタン設置
  const hsp = kintone.app.getHeaderSpaceElement();
  const txtS = new Kuc.Text({
    label: "開始日",
    visible: true,
    disabled: false,
    id: "dateS",
  });
  const txtE = new Kuc.Text({
    label: "終了日",
    visible: true,
    disabled: false,
    id: "dateE",
  });
  const btn = new Kuc.Button({
    text: "検索",
    type: "submit",
  });

  // ラジオボタン設置
  const rB = new Kuc.RadioButton({
    label: "検索モード",
    requiredIcon: true,
    items: [
      {
        label: "含む",
        value: "含む",
      },
      {
        label: "除外",
        value: "除外",
      },
    ],
    value: inirB,
  });
  hsp?.appendChild(txtS);
  hsp?.appendChild(txtE);
  hsp?.appendChild(rB);
  hsp?.appendChild(btn);

  // setIntervalで日付にdatepickerを仕込む
  const set_interval_id = setInterval(() => {
    const dateS = $("#dateS").find("input");
    const dateE = $("#dateE").find("input");
    dateS.datepicker();
    dateS.datepicker("option", { dateFormat: "yy-mm-dd" });
    dateE.datepicker();
    dateE.datepicker("option", { dateFormat: "yy-mm-dd" });

    if (queryCondition) {
      dateS.val(iniDateS);
      dateE.val(iniDateE);
    }

    clearInterval(set_interval_id);
  }, 100);

  btn.addEventListener("click", (e) => {
    txtS.value = $("#dateS").find("input").val();
    txtE.value = $("#dateE").find("input").val();

    if (!txtS.value || !txtE.value) {
      // 日付どっちかおかしかったら全部表示
      location.href = location.origin + location.pathname;
    } else {
      // 検索クエリ
      let query = "?view=" + event.viewId + "&query=";
      if (rB.value === "含む") {
        query += encodeURIComponent(
          '終了日>="' + txtS.value + '" and 開始日<="' + txtE.value + '"'
        );
      } else {// 含むのクエリのNOT
        query += encodeURIComponent(
          '終了日<"' + txtS.value + '" or 開始日>"' + txtE.value + '"'
        );
      }
      location.href = location.origin + location.pathname + query;
    }
  });

  return event;
});

```