キントーンの一覧画面で生年月日から年齢を算出する

# はじめに
キントーンの一覧に年齢を算出する処理を追加したい。

## やりたい事の例
生年月日を登録すると、年齢が自動的に算出されるような感じのもの。

| ID | 生年月日     | 年齢 |
|----|--------------|------|
| 1  | 2000年1月1日 | 24歳（コレを出力したい！） |

## アプリを作る
生年月日だけを入力できるアプリを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/06a87361-d099-7188-07d5-874f64c16b37.png)

## テストデータ作成

データを登録した状態。ここに年齢列を追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/c09b6acf-9a36-fc5a-d000-0c92d99a5c90.png)

## カスタマイズビューの作成

管理画面から一覧を選択し:heavy_plus_sign:ボタンで一覧を新規作成する。
※ 画面右あたりにあるボタン。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/bac184d8-57df-bacc-addb-ce789745be2c.png)

カスタマイズを選択し、HTMLの中に表示したい列を追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/13d14fd6-3d01-cfd5-516f-d3458154a75b.png)

実際のコード

```一覧.html
<div id="my-customized-view">
  <table border="1">
    <thead>
      <tr>
        <th>レコード番号</th>
        <th>生年月日</th>
        <th>年齢</th>
      </tr>
    </thead>
    <tbody id="my-tbody">
    </tbody>
  </table>
</div>
```

保存したら、アプリを更新し一覧画面へ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/fe6ae705-df13-55ef-2ac8-fb6403888054.png)

この状態だとなにもでなくなるので次はスクリプトを登録する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/48b82d2e-27e8-59c2-b4b5-d96bcf2ee6cc.png)

## スクリプトの登録
管理画面から **JavaScript / CSSでカスタマイズ** を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/630eed03-2c08-d1bc-6eee-9a6f707a8686.png)

JavaScriptファイルを選択して、以下のスクリプトを追加する。
**ファイルのお作法はutf8（BOMなし）**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/64a3407b-b082-eee6-9800-fde4ffaae22c.png)


```年齢計算.js
(() => {
  'use strict';
  kintone.events.on('app.record.index.show', (event) => {
    if (event.viewId !== 'ココの一覧IDは修正してください！') {
      return;
    }

    const records = event.records;
    if (records.length === 0) {
      document.getElementById('my-customized-view').innerText = '表示するレコードがありません';
      return;
    }

    const recUrl = location.protocol + '//' + location.hostname + '/k/' + kintone.app.getId() + '/show#record=';
    const myRecordSpace = document.getElementById('my-tbody');
    myRecordSpace.innerText = '';

    for (let i = 0; i < records.length; i++) {
      const record = records[i];
      const row = myRecordSpace.insertRow(myRecordSpace.rows.length);
      const cell1 = row.insertCell(0);
      const cell2 = row.insertCell(1);
      const cell3 = row.insertCell(2);

      const tmpA = document.createElement('a');
      tmpA.href = recUrl + record.レコード番号.value;
      tmpA.innerText = record.レコード番号.value;
      cell1.appendChild(tmpA);
      cell2.innerText = record.日付.value;

      // 日付から年齢を算出
      const birthDate = new Date(record.日付.value);
      const age = calculateAge(birthDate);
      cell3.innerText = age; // 年齢を表示
    }

    // 年齢計算関数
    function calculateAge(birthDate) {
      const today = new Date();
      let age = today.getFullYear() - birthDate.getFullYear();
      const m = today.getMonth() - birthDate.getMonth();
      if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
        age--;
      }
      return age;
    }

  });
})();
```

スクリプトファイルの一覧IDはカスタマイズビューの一覧IDである。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/778684f8-a764-1810-0ffa-6038a961cd45.png)



追加できたら保存して更新を行い再び一覧画面へ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/c4fa0f36-823c-b032-4dd2-83cc5829f11c.png)

年齢が計算されているのが見える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/120dc9b7-63af-732d-16df-bd31fb79b285.png)

# cssを追加
キントーンの初期画面からしてだいぶショボくなってしまったので、某AIにカッコイイ感じのレイアウトを依頼してみた。

```デザイン.css
#my-customized-view {
  font-family: 'Arial', sans-serif;
  margin: 20px;
  overflow-x: auto;
}

#my-customized-view table {
  width: 100%;
  border-collapse: collapse;
  box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
}

#my-customized-view th, #my-customized-view td {
  text-align: left;
  padding: 8px;
  border-bottom: 1px solid #ddd;
}

#my-customized-view th {
  background-color: #4CAF50;
  color: white;
}

#my-customized-view tr:nth-child(even) {
  background-color: #f2f2f2;
}

#my-customized-view tr:hover {
  background-color: #ddd;
}

/* アニメーションの追加 */
@keyframes gradientBackground {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

#my-customized-view th {
  background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
  background-size: 200% 200%;
  animation: gradientBackground 5s ease infinite;
  color: white;
  border: none;
}

```

これを、先程の管理画面へアップロードして更新する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/6cd9ba0d-eae8-3329-4c0d-4a4a45b15168.png)

AIさんのお陰でいい感じのデザインになった。

![sample4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119455/ed94c24f-bca3-897e-2ff6-73b43371cc8e.gif)


## 参考文献
サイボウズのdeveloper networkの資料だけで出来た。

* [第 7 回 カスタマイズビューを利用してみよう](https://cybozu.dev/ja/kintone/miscellaneous/hello-kinapi/customized-view/)