バックエンドをノーコードで「タスク管理ツール」を作った話

# はじめに
皆さん、仕事ををやる上でタスク管理ツールを使われると思います。
RedmineとかBacklogとか使ってますか？

そんな感じのものを
下記を使えばノーコードで作れるんじゃないのかなと思って作ってみた次第です。
[CRUDのWebApi開発は不要！Json登録でWebApiを作れるシステムを作った話](https://qiita.com/HawkClaws/items/6f5a6938318fb7d7983d)

また不完全ですが、作れました！！

# 作ったもの
みなさんが凄いもの作ってくれることを期待して、あえて90年代のデザインにしました😁

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/4ffae50a-2abb-bf35-5d3e-df7b7dbdb4d2.png">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/4d5c06c2-59b0-4120-f695-59efe7caddcc.png">

# 公開WebApi

これと同じような感じで使えます。
[エンジニア・プログラマにしか使えないSNSを作ってみた話](https://qiita.com/HawkClaws/items/599d7666f55e79ef7f56)

####エンドポイント
`https://versatileapi.herokuapp.com/api/task_management`

####ユーザー
登録
`POST　/user`

更新
`PUT　/user/:user_id`

取得
`GET　/user/:user_id`

全件取得
`GET　/user/all`
ODataクエリが使用できます。（全て対応はしていません）

スキーマ

```user.json
{
    "additionalProperties": false,
    "type": "object",
    "properties": {
        "name": {
            "description": "名前",
            "type": "string",
            "minLength": 1,
            "maxLength": 50
        }
    },
    "required": [
        "name"
    ]
}
```

####カテゴリ
登録
`POST　/category`

更新
`PUT　/category/:category_id`

取得
`GET　/category/:category_id`

全件取得
`GET　/category/all`
ODataクエリが使用できます。（全て対応はしていません）

スキーマ

```user.json
{
    "additionalProperties":false,
    "type": "object",
    "properties": {
        "category_name": {
            "description": "カテゴリ名",
            "type": "string",
            "minLength": 1,
            "maxLength": 50
        }
    },
    "required": [
        "category_name"
    ]
}
```

####タスク
登録
`POST　/task`

更新
`PUT　/task/:task_id`

取得
`GET　/task/:task_id`

全件取得
`GET　/task/all`
ODataクエリが使用できます。（全て対応はしていません）

スキーマ

```user.json
{
    "additionalProperties": false,
    "type": "object",
    "properties": {
        "title": {
            "description": "タスク名",
            "type": "string",
            "minLength": 1,
            "maxLength": 140
        },
        "description": {
            "description": "タスクの内容",
            "type": "string",
            "minLength": 1,
            "maxLength": 1000
        },
        "in_charge_user_id": {
            "description": "担当者Id",
            "type": "string",
            "minLength": 36,
            "maxLength": 36
        },
        "start_datetime": {
            "description": "開始日",
            "type": "string",
            "format":"date"
        },
        "end_datetime": {
            "description": "期限日",
            "type": "string",
            "format":"date"
        },
        "scheduled": {
            "description": "予定",
            "type": "number"
        },
        "achievements": {
            "description": "実績",
            "type": "number"
        },
        "status": {
            "description": "状態",
            "type": "string",
            "enum": [
              "waiting",
              "working",
              "pending",
              "completed",
              "discontinued"
            ]
        },
        "category_id": {
            "description": "カテゴリId",
            "type": "string",
            "minLength": 36,
            "maxLength": 36
        }
    },
    "required": [
        "title",
        "status"
    ]
}
```

# 暫定で作ったフロントエンドのHTML
一応公開しておきます。vue.jsで作りました

```task-list.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>タスク管理ツール</title>
</head>

<body>
    <h2>タスク一覧</h2>
    <div id="app">
        <ul id="example-1">
            <li v-for="item in taskList" :key="taskList.id">
                <a :href="'./task-detail.html?taskid='+ item.id"> {{ item.title }}</a>
            </li>
        </ul>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
    <script>
        const api = "https://versatileapi.herokuapp.com/api/task_management/";
        const app = new Vue({
            el: "#app",
            data: {
                taskList: []
            },
            async mounted() {
                //可読性は良いけど、待たないほうが早い
                let taksInfo = await (await fetch(api + "task/all")).json();

                taksInfo.forEach(res => {
                    this.taskList.push(res);
                });
            },
            methods: {
            }
        });
    </script>
</body>

</html>
```

```task-detail.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>タスク管理ツール</title>
</head>

<body>
    <div id="app">
        <h2>タスク詳細</h2>
        <p>タイトル：{{title}}</p>
        <p>チケット内容：{{description}}</p>
        <p>担当者：{{in_charge_user}}</p>
        <div style="display: flex;">
            <p>開始日：{{start_datetime}}</p>
            <p>期限日：{{end_datetime}}</p>
        </div>
        <div style="display: flex;">
            <p>予定：{{scheduled}}</p>
            <p>実績：{{achievements}}</p>
        </div>
        <p>状態：{{status}}</p>
        <p>カテゴリ：{{categoryName}}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
    <script>
        const api = "https://versatileapi.herokuapp.com/api/task_management/";
        const app = new Vue({
            el: "#app",
            data: {
                title: "",
                description: "",
                in_charge_user: "",
                start_datetime: "",
                end_datetime: "",
                scheduled: "",
                achievements: "",
                status: "",
                categoryName: "",
            },
            async mounted() {
                let taskId = this.getUrlQueries()["taskid"];

                //可読性は良いけど、待たないほうが早い
                let taksInfo = await (await fetch(api + "task/" + taskId)).json();

                let userName = "";
                if (taksInfo["in_charge_user_id"] != null)
                    userName = (await (await fetch(api + "user/" + taksInfo["in_charge_user_id"])).json())["name"];
                let categoryName = "";
                if (taksInfo["category_id"] != null)
                    categoryName = (await (await fetch(api + "category/" + taksInfo["category_id"])).json())["category_name"];

                this.title = taksInfo["title"];
                this.description = taksInfo["description"];
                this.in_charge_user = userName;
                this.start_datetime = taksInfo["start_datetime"];
                this.end_datetime = taksInfo["end_datetime"];
                this.scheduled = taksInfo["scheduled"];
                this.achievements = taksInfo["achievements"];
                this.status = taksInfo["status"];
                this.categoryName = categoryName;
            },
            methods: {
                getUrlQueries: function () {
                    var queryStr = window.location.search.slice(1);
                    queries = {};
                    if (!queryStr) {
                        return queries;
                    }
                    queryStr.split("&").forEach(function (queryStr) {
                        var queryArr = queryStr.split("=");
                        queries[queryArr[0]] = queryArr[1];
                    });

                    return queries;
                }
            }
        });
    </script>
</body>

</html>
```

<a id="#reference"></a>
# 参考文献
- [エンジニア・プログラマにしか使えないSNSを作ってみた話](https://qiita.com/HawkClaws/items/599d7666f55e79ef7f56)
- [CRUDのWebApi開発は不要！JsonでWebApiを作れるシステムを作った話](https://qiita.com/HawkClaws/items/6f5a6938318fb7d7983d)