来週オープンする自分のカフェの発注web appを、GitHubからcloneしてVue.jsで作ってみた。

# Vue.jsで何かつくる。
今回のProtooutstudioの課題で、GitHub上に公開されているネタからVue.jsで何かを作るというものでした。
そこで、せっかく来週オープンするカフェに関わるものを作ろうと考えました。
完成品がこちら。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">あんまり上手くいかなかったけど、カフェの材料発注ウェブアプリをvue.jsで自作。<a href="https://twitter.com/hashtag/protoout?src=hash&amp;ref_src=twsrc%5Etfw">#protoout</a> <a href="https://t.co/cP6XwDCtvh">pic.twitter.com/cP6XwDCtvh</a></p>&mdash; 北城雅照@足立慶友整形外科 (@kutuyanomusuko) <a href="https://twitter.com/kutuyanomusuko/status/1339292680644251648?ref_src=twsrc%5Etfw">December 16, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

見た目の修正はまだまだ必要ですが、必要な昨日はとりあえず実装できたと思います。

# GitHubを検索し、cloneする。
いろいろと検索し、使えそうなものを発見したので、今回は[こちら](https://github.com/jamesgeorge007/Vue-ToDo)を使います。
動作のサンプルは[こちら](http://vuex-todo-sample.surge.sh/)です。

右上のグリーンのCodeからURLを取得し、下記のコマンドを入力します。
![スクリーンショット 2020-12-16 23.20.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/106cddd9-db7d-c264-9b56-4fecf1a799b4.png)

```ターミナルコマンド
$ git clone　https://github.com/jamesgeorge007/Vue-ToDo.git
```

その後、使用するディレクトリーに移動し、

```ターミナルコマンド
$ cd Vue-ToDo
```

必要なパッケージをインストールします。

```ターミナルコマンド
$ npm install
```

試しに、下記のコマンドを入力して、ローカルサーバーで動かします。

```ターミナルコマンド
$ npm run serve
```

下記のような表記が出ればOK。
LocalのURLをクリックすると、サンプルと同様なブラウザが立ち上がります。
![スクリーンショット 2020-12-16 23.37.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/b28ae50d-f777-bf04-5451-921c868a449d.png)

# サンプルコードを加工し、オリジナルにする。
vomponentsの中の、Stats.vueとToDo.vueを下記のように加工しました。

```vuejs:Stats.vue
<template>
  <div>
    <h1 class="display-3 font-weight-bold font-color-red">全削除</h1>
    <h2>項目を全消去したい時のみ押してください。</h2>
    <p>発注希望、及び、発注済みが全部で {{ countTasks }} 項目あります。</p>
    <v-btn x-large color="red" v-on:click="removeAllTasks">全削除</v-btn>
    <p>{{ msg }}</p>
  </div>
</template>

<script>
import { mapGetters, mapMutations, mapActions, mapState } from "vuex";

export default {
  name: "Stats",
  computed: {
    ...mapGetters(["countTasks"]),
    ...mapState(["msg"])
  },
  methods: {
    ...mapMutations(["REMOVE_ALL"]),
    ...mapActions(["removeAll", "updateStatus"]),
    async removeAllTasks() {
      await this.removeAll();
      this.updateStatus();
    }
  }
};
</script>

<style></style>

```
```vuejs:ToDo.vue
<template>
  <div>
    <div class="maemae">
     <h1 class="display-2 font-weight-black">Hawaiian Cafe Mae'emae'e</h1>
      <img src="./maemaeL.png">
    </div>
     <div>
      <Stats />
    </div>
    <div class="left">
      <v-container>
        <h1 class="display-2 font-weight-black">{{ title }}</h1>
        <v-form @submit.prevent="addTask">
          <v-text-field label="発注希望品" v-model="newTask"> </v-text-field>
        </v-form>
        <v-list
          v-for="(task, index) in tasks"
          v-bind:key="index"
          v-if="!task.done"
        >
          {{ task.text }}<v-btn dark
            v-on:click="doneTasks(index, true)"
          >発注</v-btn>
          <v-btn v-on:click="removeTasks(index)">削除</v-btn>
        </v-list>
      </v-container>
    </div>
    <div class="right">
      <v-container>
        <h1 class="display-2 font-weight-black">発注済み</h1>
        <v-list
          v-for="(task, index) in tasks"
          v-bind:key="index"
          v-if="task.done"
        >
          {{ task.text }}
          <v-btn
            dark
            v-on:click="doneTasks(index, false)"
          >発注リストに戻す</v-btn>
          <v-btn v-on:click="removeTasks(index)">削除</v-btn>
        </v-list>
      </v-container>
    </div>
   
  </div>
</template>

<script>
import { mapState, mapMutations, mapActions } from "vuex";
import Stats from "./Stats.vue";

export default {
  name: "ToDo",
  data() {
    return {
      newTask: ""
    };
  },
  computed: {
    ...mapState(["title", "tasks"])
  },
  components: {
    Stats
  },
  methods: {
    ...mapMutations(["ADD_TASK", "DONE_TASK"]),
    ...mapActions(["removeTask", "removeStatus"]),
    addTask: function() {
      if (!this.newTask) {
        alert("Kindly provide a task");
        return;
      }
      this.ADD_TASK(this.newTask);
      this.newTask = "";
      this.removeStatus();
    },
    removeTasks: function(task) {
      this.removeTask(task);
    },
    doneTasks: function(index, done) {
      this.DONE_TASK({
        index,
        done
      });
      this.removeStatus();
    }
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.left {
  float: left;
  padding-left: 10vw;
}
.right {
  float: right;
  padding-right: 10vw;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: block;
  margin: 0 10px;
}
i:hover {
  cursor: pointer;
}
</style>
```

```javascript:store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    title: "発注希望",
    msg: "",
    tasks: [
      {
        text: "オーガニックコーヒー 5kg",
        done: false
      },
      {
        text: "ハワイアンコーヒー 10packs",
        done: false
      },
      {
        text: "ハワイアンフレーバーコーヒー 10packs",
        done: false
      },
      {
        text: "アップグレードコーヒー　ココナッツ　3packs",
        done: false
      },
      {
        text: "アップグレードコーヒー　ココア　3packs",
        done: false
      },
      {
        text: "アップグレードコーヒー　ダークモカ　3packs",
        done: false
      }
    ]
  },

  getters: {
    countTasks: state => {
      return state.tasks.length;
    }
  },

  mutations: {
    ADD_TASK: (state, task) => {
      state.tasks.push({
        text: task,
        done: false
      });
    },
    REMOVE_TASK: (state, task) => {
      state.tasks.splice(task, 1);
      state.msg = "Successfully removed!";
    },
    REMOVE_ALL: state => {
      state.tasks = [];
    },
    REMOVE_STATUS: state => {
      state.msg = "";
    },
    UPDATE_STATUS: state => {
      state.msg = "項目を全て削除しました。";
    },
    DONE_TASK: (state, obj) => {
      state.tasks[obj.index].done = obj.done;
    }
  },

  actions: {
    removeTask: (context, task) => {
      context.commit("REMOVE_TASK", task);
    },
    removeAll({ commit }) {
      return new Promise(resolve => {
        setTimeout(() => {
          commit("REMOVE_ALL");
          resolve();
        }, 100);
      });
    },
    removeStatus: context => {
      context.commit("REMOVE_STATUS");
    },
    updateStatus: context => {
      context.commit("UPDATE_STATUS");
    }
  }
});

```
# 完成

```ターミナルコマンド
$ npm run build
```
を行い、完成したdistファイルをnetlifyにデプロイしました。
下記のQRから見ることができます。ご興味のある方はぜひ。
![QR_150831.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581193/1d27f1c8-3875-66b6-39b3-a084e519531d.png)


# 今後の課題
できれば、発注のボタンを押した時にwebhookなどを飛ばせる設定にしたかったのですが、うまく行かなかったので、今後修正したいと思います。

# その他の記事
[近すぎると小池都知事が『密です。』と連呼するデバイスを作ったら腹筋が崩壊したので、皆さんにも試して欲しい。](https://qiita.com/Teru_3/items/4570bd76d8feeaa37db4)

[誰が使うかわからないけど、膝のレントゲン写真を送ったら、その膝がどの程度痛んでいるのか教えてくれるラインbotを作ってみた。](https://qiita.com/Teru_3/items/80cecd138860fbd0c924)

[猪木の名言で元気をくれるbotを作ったら、想定外の応答で笑いが止まらなくなったから、ぜひ試して欲しい。](https://qiita.com/Teru_3/items/4d0c32fbbc609695e4b8)

[twitterが使えないというIntegromatの弱点を克服し、最強化する方法を書くので試して欲しい。](https://qiita.com/Teru_3/items/9530447a181bf7ca7cc2)