Vue.jsとFirebase Firestoreで洗濯物干し場のWEB予約アプリを1日で作ってみた。

夏休みの自主課題です。
我が家は夫婦で二人暮らしなのですが、自分のことは自分でする精神のもと洗濯物は各自で行っています。当然洗濯機は一つですし洗濯物干し場も一つですが、その日誰が洗濯するかの管理は夫婦のアイコンタクト頼りでした。
最近FirestoreとVuejsの連携に興味があったのとVuetifyの練習のために洗濯物干し場(というか様々な施設)の利用状況を管理し、予約ができるシステムをWEBアプリで作ってみました

なお個人的な都合により夏休みが1日しか無いので、実装時間は1日です。
あと、ソースコードは公開してませんが、ご要望があればコメントください。

## 使用するもの
* Vue.js
* Firebase
* Vuetify

## 作ったもの
![Aug-14-2019 22-06-36.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/b96ce6ed-b9bc-6d18-ba5b-c0c16a1323a5.gif)

## 環境
時間もお金も無いのでバックエンドはFirestoreに全部お任せ。デプロイもFirebase deployに任せます。当然認証も。

vue-cliを使ってプロジェクトを作ります。ついでにvuetifyも入れときます。

```
$ vue create my_home_management
$ cd my_home_management
$ vue add vuetify
```

とりあえずvue create はdefaultで作ります。

スクリプトが走っている間に[Firebase](https://firebase.google.com/?hl=ja)側を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/153fafb7-ac7d-9581-6ff8-3fd17cef8326.png)

とりあえず解析は無しで

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/456b548a-b556-23a7-3d43-9433f129ab35.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/f6d9e2a4-cee7-e953-c2e9-dd7bc2d5734d.png)

データベースもテストモードで作成。ロケーションはどこでもいいのでデフォルトで。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/b3b6272d-c736-0640-12fa-1a0bd331d1dc.png)

できたのでコレクションを作ります。
今回は設備の予約をメインに考えるので、設備(Facilities)と予約(Bookings)で。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/98e47afd-f043-3062-4c2e-b53408c9ff61.png)

vue createもいい加減終わっていると思うので、firebaseの初期設定をします。

今回はファイルを分けずにmain.jsにサクッといれていきます。


```src/main.js
import Vue from "vue";
import firebase from "firebase/app";
import "firebase/firestore";
import App from "./App.vue";
import vuetify from "./plugins/vuetify";

Vue.config.productionTip = false;

Vue.use(VueFire);

const firebaseApp = firebase.initializeApp({
  apiKey: "XXXXXXXXXXXXXXXXXXXXXXXX",
  authDomain: "XXXXXXXXXXXXXXXXXXXXXXXX",
  databaseURL: "XXXXXXXXXXXXXXXXXXXXXXXX",
  projectId: "XXXXXXXXXXXXXXXXXXXXXXXX",
  storageBucket: "XXXXXXXXXXXXXXXXXXXXXXXX",
  messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXXXX",
  appId: "XXXXXXXXXXXXXXXXXXXXXXXX"
});

const firestore = firebaseApp.firestore();

new Vue({
  vuetify,
  render: h => h(App)
}).$mount("#app");

```
これで環境はOKです。

##完成イメージから全体像を把握する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/a682cc43-1a47-204d-9975-f6903a9f77d6.png)

###全体像から必要そうなコンポーネントと機能を洗い出す。

* ログイン機能
* 左側上部の時計
* リストタブコンポーネントは最新５件を表示。
* カレンダータブは施設全ての一月の予定を表示。
* Bookingタブで予約
* 右側に状況表示
* 右側の今すぐ利用するで即時予約情報を入れる

必要そうなコンポーネントは

* Login
* Clock
* List
* Calendar
* Booking
* MainWindow(右側のウィンドウ)

だいたい構想が決まったら、ガリガリ書いていきます。
まずは予約機能のBookingから。

###予約機能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/c4339a57-fc79-ea37-d510-36e62b02c199.png)
daytopickerはVuetifyをそのまんま使いますが、あらかじめ表示されるようにしました。
施設(場所)はとりあえずfirebaseの画面から直接いれましたが、拡張できるようにIDは一意のIDとして作ります。
予約はFirestoreのサブコレクションを使い施設のサブコレクションとしましたが、Calendarに一月分表示したいので、ルートにもBookingsコレクションを使ってbatchで両方に書き込んでます。その際、ルート側のBookingsコレクションには施設IDや名前をいれて冗長化しています。SQLでは絶対にしない考えですが、Firestoreでは「見たままを保存する」という考えで作りましたが、慣れると簡単に思えます。

###カレンダー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/68ad41e5-474c-11ce-8d50-e6c94f603453.png)
こちらもVuetifyのカレンダー機能をそのまま利用。
eventsにデータを用意して表示。範囲指定もできるのですごく便利。

###リスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/9acbc0c3-e6a4-54a9-8644-6c303af5d66c.png)
黒塗りには名前が入ります。

Firestoreのorderbyやページネーションの仕組みを利用。今回は５件だけだが、複数件表示するときはすごく大変そう。

###メイン表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/33571a13-4821-591e-418b-2efe029b46ab.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/b17d7f9b-1954-5afa-e099-37aec6830d06.png)

予約されているときは利用不可の表示。
今すぐ利用するを押すとすぐに利用不可にできる。
裏ではFirestoreに現在時刻から23:59までの予約をいれている。
予約解除は逆に終了時刻を現在時刻としている。

###ログイン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65643/ab574d1b-b717-58ab-e130-af744f400ce8.png)

左側のハンバーガーメニューにログイン機能。
Firebase Authenticationを利用してGoogleログインオンリー。
displaynameとか拾えるので名前表示がすぐ使えるのは便利。

##テクニカルだった部分

###client side join
Firestoreはクエリで一括で持ってくるよりも、こまめに読み込みをかけた方がお金もかからないし早い。そのためにはカレンダーとかでの一覧表示は書き込み時に冗長化してやると便利。
結果、読み込みはすごく早い。

###VueとFirebase
リアルタイムで反映するonSnapshotはVueのディバイディングと相性がいい。更新処理なんて一切作らなくてもリアルタイム更新してくれる。

###Vuexはやっぱり欲しい
作っている途中からVuexが使いたい状況がちらほらあった。よっぽど制限がないのであれば素直に使った方がよさそう。

###firebase hostingが便利。
上で一通り作ったものをそのままfirebase hostingにあげました。

```
$ npm install -g firebase-tools
$ firebase login
$ firebase init
$ firebase deploy
```
これだけでhttps付きで公開できる。すごく簡単になったものだなぁ。


##妥協したところ

###timestampの取り扱い
firebaseのtimestampの取り扱いが独特。最近までコロコロ変わってたのが原因見たい。結局サーバーサイドのタイムスタンプが取れず挫折。

###Vuetify
もっと便利な使い方がありそうだけど、時間の関係で中途半端になってしまった。

##まとめ
1日でこれだけ作れる便利な世の中になったものだなぁ。実用性はともかく。