Articles tagged with: Calendar Plus

Cybozu OfficeのスケジュールをCalendar Plusで(だいぶ)再現!


カレンダーPlus Advent Calendar 2020の5日目の記事です。

  Topへ↓

先日のCybozu Days 2020 Osakaの後、Calendar PlusのAdvent Calendarに空きがあると聞き、やってみましょうか?と言ってしまいまして。
言った以上はエントリーせねば。というわけで翌日、神戸で商談する前に中華街で飯を食べながら記事にエントリー宣言。

さて、エントリーしたのはいいが、何を書けばええんやろ?しかもエントリーしたタイミングは前日やし(^^;)。これはひょっとして勇み足をやらかしてしまったのではなかろうか。

迷いを振り切った私が商談に臨むと、なんとお客様がCybozu Officeのスケジュールを愛用しており、それをkintoneに載せ替えたいとのこと。しかもkintone標準の標準のカレンダー機能は表現に乏しく、Calendar Plusを検討してみたけど、プラグイン設定の色設定の種類が少なくてなぁ。と悩みを吐露されまして。商談からネタが転がってきた瞬間です。
これはCybozu Officeのスケジュールアプリの画面です。

私は今までも何回かCalendar Plusは提案してきました。けれど、本格的なユーザーではないし、弊社のkintone納入実績からするとCalendar Plusの納品実績は一割もありません。熟練のユーザーではないのです。

が、Calendar Plus JavaScript APIを使ったカスタマイズの経験はあります。API使えば、ある程度ならCybozu Officeの体裁にCalendar Plusを合わせられるんちゃうの?という見切り発車でした。

Calendar Plusのプラグイン設定画面

  Topへ↑

うーむ、確かにCalendar Plusの色設定は12種類か。

そして、お客様のCybozu Officeのスケジュールの色設定で設けた種別は25種類。つまり倍。

確かにお客様がkintoneへの移行をためらわれるのもわかるなあ。

アプリを作る

  Topへ↑

じゃあ、ちょっと試しにアプリを作ってみよう。こんな感じで。

あらよっと、Cybozu Officeと同じようにレコードを登録してみました。さて、Calendar Plusで見るとどうなる・・・?

あかん・・・Calendar Plus側の色設定をしていないのですべて同じ緑地になってる。
しかも予定の状況がカレンダー上に表示されておらず、直行なのか直帰なのかが一目で区別できない。
やはり見にくいなあ・・・・これは、Calendar Plus JavaScript APIの出番かな。

コーダーモード発動

  Topへ↑

ということで、ガリガリとコーディングすること2時間。うん、こんな感じになりました。


というわけで最低限、記事にできるところまでこぎつけたのでいったん寝ます。アップ当日になってしまいましたが、AM2時前なのにもう眠い。年や。

プログラム開示

  Topへ↑
さて、アップ当日の朝、旅に出かける前に少しだけ記事を書きまして。
ここにコードを開示しておきます。すみません。洗練されたコードとは程遠くて・・・・。

(function () {
  "use strict"

  // 関数本体
  const calenderformat = function (event) {
    // Calendar Plusのイベントリスナー定義
    const calendareventslist = ["cp.event.show"];
    // kintoneのイベントの書き方と似ててわかりみ
    calendarplus.events.on(calendareventslist, function (calendarevent) {
      // kintoneのイベントのjson(上記イベントで対象データループしてくれます)
      const record = calendarevent.record;
      // kintoneのイベントの要素
      const element = calendarevent.element;
      // テーブルだけkintoneのイベントの要素
      const tdelement = document.getElementsByClassName("fc-event-container");
      // Cybozu Officeの時刻表示に合わせるため(ださいかも)
      const starttime =
        ("00" + new Date(record.開始時刻.value).getHours()).slice(-2) +
        ":" +
        ("00" + new Date(record.開始時刻.value).getMinutes()).slice(-2);
      const endtime =
        ("00" + new Date(record.終了時刻.value).getHours()).slice(-2) +
        ":" +
        ("00" + new Date(record.終了時刻.value).getMinutes()).slice(-2);
      // 左端の行高がCalendar Plusでは縮まるため、Cybozu Officeの行高っぽくする
      tdelement[0].style.minHeight = "90px";
      tdelement[1].style.minHeight = "90px";
      tdelement[2].style.minHeight = "90px";
      tdelement[3].style.minHeight = "90px";
      tdelement[4].style.minHeight = "90px";
      // リンクするためのurl
      const baseurl =
        kintone.api.url("/k/").replace(".json", "") +
        kintone.app.getId() +
        "/show#record=" +
        record.$id.value;
      // 予定の状況にあわせてSwitch(ださい)
      switch (record.予定の状況.value) {
        case "ーーー":
          // 枠の背景色
          element[0].style.backgroundColor = "#FFFFFF";
          // 文字色(本当はhoverとかの定義も必要。そもそもcssにまとめたほうがよい)
          element[0].style.color = "#006bac";
          // 折り返してくれなかったので折り返し(そもそもcssにまとめたほうがよい)
          element[0].style.whiteSpace = "break-spaces";

          // 終日フラグかどうか
          if (record.終日.value.length > 0) {
            // 頭に・をつける
            element[0].innerText = "・" + element[0].innerText;
          } else {
            // 頭に時刻をつけて改行
            element[0].innerHTML =
              starttime +
              "-" +
              endtime +
              "
 " +
              '<a href="' +
              baseurl +
              '">' +
              record.予定.value +
              "</a>";
          }
          break;
        case "直行":
          // 枠の背景色
          element[0].style.backgroundColor = "#ebf7ef";
          // 枠の線色
          element[0].style.borderColor = "#d8f2e0";
          // 文字色(本当はhoverとかの定義も必要。そもそもcssにまとめたほうがよい)
          element[0].style.color = "#006bac";
          // 折り返してくれなかったので折り返し(そもそもcssにまとめたほうがよい)
          element[0].style.whiteSpace = "break-spaces";
          // 終日フラグかどうか
          if (record.終日.value.length > 0) {
            // 頭に・をつけ、予定の状況を枠で囲む
            element[0].innerHTML =
              "・<span style="background-color:#32a759;color:#fff;" class="scheduleMarkEventMenu">" +
              record.予定の状況.value +
              "</span>" +
              record.予定.value;
          } else {
            // 頭に時刻をつけ、予定の状況を枠で囲む(以下コメントは省略)
            element[0].innerHTML =
              starttime +
              "-" +
              endtime +
              "
 " +
              '<a href="' +
              baseurl +
              '">' +
              '<span style="background-color:#32a759;color:#fff;" class="scheduleMarkEventMenu">' +
              record.予定の状況.value +
              "</span>" +
              record.予定.value +
              "</a>";
          }
          break;
        case "直帰":
        case "直行/直帰":
          element[0].style.backgroundColor = "#e8f2fc";
          element[0].style.borderColor = "#dae8f7";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#3182dc;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "転送":
        case "納品":
        case "設定":
        case "設置":
        case "移設":
          element[0].style.backgroundColor = "#f4fce8";
          element[0].style.borderColor = "#e5f2d3";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#83cb26;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "点検":
          element[0].style.backgroundColor = "#fff4e3";
          element[0].style.borderColor = "#fae8cd";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#ef9201;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "デモ":
        case "営業":
        case "商談":
        case "打合せ":
        case "来客":
          element[0].style.backgroundColor = "#faf6f2";
          element[0].style.borderColor = "#f2e9df";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#c3a88b;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "当番":
          element[0].style.backgroundColor = "#fcfae8";
          element[0].style.borderColor = "#f2efd3";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#dfc506;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "休み":
        case "有休":
        case "リフ":
        case "代休":
          element[0].style.backgroundColor = "#ffebeb";
          element[0].style.borderColor = "#fcdede";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#f44848;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "午前半休":
        case "午後半休":
          element[0].style.backgroundColor = "#fff2f5";
          element[0].style.borderColor = "#f7e9d2";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#f3a4b4;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "土曜当番出勤":
        case "出張":
        case "会議":
          element[0].style.backgroundColor = "#f5edfc";
          element[0].style.borderColor = "#ebe1f5";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#b592d8;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
        case "土曜振休":
          element[0].style.backgroundColor = "#ffebeb";
          element[0].style.borderColor = "#fcdede";
          element[0].style.color = "#006bac";
          element[0].style.whiteSpace = "break-spaces";
          element[0].innerHTML =
            "・<span style="background-color:#f44848;color:#fff;" class="scheduleMarkEventMenu">" +
            record.予定の状況.value +
            "</span>" +
            record.予定.value;
          break;
      }
      return calendarevent;
    });
  };
  // kintone自体のイベント
  const eventslist = ["app.record.index.show"];
  kintone.events.on(eventslist, (event) => {
    // ビューが一致したら本体の関数呼び出し
    if (event.viewId === 5543487) {
      calenderformat(event);
    }
    return event;
  });
})(jQuery);

あと、cssも全体のフォントに関する設定と予定の状況の枠に対して設定しています。

div#calendarPlus {
    font-family:"ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", ヒラギノ角ゴシック, "Hiragino Sans", メイリオ, Meiryo, "MS Pゴシック", "MS PGothic", sans-serif;
    font-size:14.4px;
    font-weight:400;
}
    
div#calendarPlus span.scheduleMarkEventMenu{
    display: inline-block;
    padding: 1px 2px;
    color: #fff;
    margin: 0 1px 2px 1px;
    line-height: 1.2em;
    border-radius: 2px;
    font-size: 12px;
    vertical-align: middle;
}
    

まとめ

  Topへ↑

てなわけで、日中は記事のことは一切忘れて徳島のあちこちをめぐっておりました。で、21時前に実家に帰ってきました。
では、早速記事の続きを。うん、あらためて確認しても表示はこんな感じで大丈夫かと。

本当は実装すべきなのは週だけでなく、他の日や月でも同じように表示できるか試さねばならないですね。また、Cybozu Officeのスケジュールアプリの日時と曜日の行見出しの部分はCalendar Plusでは再現していません。
まあこんな感じですが、記事の主旨は満たせたと判断し、今宵はここまでにしてアップしたいと思います。

あとは、金曜日に商談したお客様がこの実装でご満足いただけるかどうかでしょうか。なにしろ、お客様に商談の御礼のメールを送るより先に、こちらの記事をアップしてしまったので(^^;)。おいおい。

本稿がCybozu Officeからkintoneへの移行をお考えの皆様にとって少しでも手助けになれば幸せです。

もしお困りの際は弊社までご連絡くださいませ。

当エントリーの参考にさせていただいたブログ

  Topへ↑

最後になりましたが、このエントリー作成にあたり、以下のサイトからの情報を参考にさせていただきました。
ラジカルブリッジの斎藤さんにはいつも感謝です。内容にやばい内容が含まれていたら直しますね。
ありがとうございました。

 Calendar Plus JavaScript APIのリファレンス