戸締り確認の不安を解消するPWAアプリを作ってみた

# はじめに

初めての投稿になります。よろしくお願いしますm(_ _)m。

結論としてタイトルの結果、完成したのが以下のウェブアプリです。基本的にスマホから見るのでブラウザ越しなのですが、PWA アプリに対応しているのでホーム画面に張り付けても使えます。

[戸締り確認くん](https://tojimari.netlify.com)

### 日本語の表示

![20190812_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148315/2d38e7c5-2ca6-3c57-2455-a44fd5b3263e.png)

### 英語の表示

![20190812_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148315/cc02f97e-96ab-4ea7-0643-6adf4c027572.png)


これは以前、[Togetter で見かけた記事](https://togetter.com/li/1344916) を見て、これがスマホアプリだったらいいのに・・・と思っていたことがきっかけでこの度チャレンジしてみました。

## 構成

静的サイトオンリーで作っています。サーバーサイドはもうちょっと勉強しないと分からないです。

### 基本

- HTML, CSS, JavaScript
- bootstrap
- bootstrap-toggle
- js.cookie.js

### その他

- PWA 対応
- Google Analytics
- GDPR
- Netlify
- 日本語、英語切り替え

アイディア自体はすごくシンプルで、各項目を表形式で表示させて、各項目に対してオンオフボタンと、ボタンを押したときの更新日を表示させるだけです。後はこれを保存したり更新したりすれば出来上がり！という仕様です。

自分は元々 C#/VB でデスクトップ開発していたので、そんなにウェブ系には明るくありませんでした。でもこの仕様でいけると思ったので、HTML を勉強中の身でも作れるのは？と思いました。

後はググって使い方覚えて組み合わせて組み込む。のループでした。完成した今見返してみると、全体的に初級レベルだと思うのですが、作っている最中は１つ１つが知らないことだったので、初心者なりに苦労してつまづいていたりしました。

ちなみに、ググった結果多く参考にさせていただいたサイトがこの Qiita でした。本当に皆様のおかげです！ありがとうございます！優しい世界！

## 技術的な話

本当におこがましいのですが、アイディアがあって無いようなものですので、せめてソースコードを載せて還元させていただこうかと思います。完全ソースではなく、主要部分のみ抜粋です。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>戸締まり確認くん</title>
        <link rel="stylesheet" href="css/bootstrap.css">
        <link rel="stylesheet" href="css/bootstrap4-toggle.css">
        <script type="text/javascript" src="js/jquery-3.4.1.js"></script>
        <script type="text/javascript" src="js/bootstrap.bundle.js"></script>
        <script type="text/javascript" src="js/bootstrap4-toggle.js"></script>
        <script type="text/javascript" src="js/js.cookie.js"></script>
        <script type="text/javascript" src="js/main.js"></script>
    </head>
    <body>
        <div class="container">

            <h1 id="title">戸締まり確認くん</h1>
            <div>
                <ul class="list-inline">
                    <li class="list-inline-item"><a href="" onclick="toEnglish()">English</a></li>
                    <li class="list-inline-item"><a href="" onclick="toJapanese()">日本語</a></li>
                </ul>
            </div>
            <br>

            <p id="description">On/Off データはブラウザの Cookie（30日間）として保存・表示しています。手動で切り替えてお使いください。</p>
            <br>

            <h2 id="normal">日常</h2>
            <table class="table table-striped">
                <tr>
                    <td><label id="lbl1a"></label></td>
                    <td><input type="checkbox" id="chk1" data-toggle="toggle" data-width="80" data-on="OK" data-off="NG"></td>
                    <td><label id="lbl1b">　　　　　　　　</label></td>
                </tr>
                <tr>
                    <td><label id="lbl2a"></label></td>
                    <td><input type="checkbox" id="chk2" data-toggle="toggle" data-width="80" data-on="OK" data-off="NG"></td>
                    <td><label id="lbl2b"></label></td>
                </tr>
                <tr>
                    <td><label id="lbl3a"></label></td>
                    <td><input type="checkbox" id="chk3" data-toggle="toggle" data-width="80" data-on="OK" data-off="NG"></td>
                    <td><label id="lbl3b"></label></td>
                </tr>
                <tr>
                    <td><label id="lbl4a"></label></td>
                    <td><input type="checkbox" id="chk4" data-toggle="toggle" data-width="80" data-on="OK" data-off="NG"></td>
                    <td><label id="lbl4b"></label></td>
                </tr>
            </table>
            <br>

            <h2 id="vacation">長期留守</h2>
            <table class="table table-striped">
                <tr>
                    <td><label id="lbl5a"></label></td>
                    <td><input type="checkbox" id="chk5" data-toggle="toggle" data-width="80" data-on="OK" data-off="NG"></td>
                    <td><label id="lbl5b">　　　　　　　　</label></td>
                </tr>
                <tr>
                    <td><label id="lbl6a"></label></td>
                    <td><input type="checkbox" id="chk6" data-toggle="toggle" data-width="80" data-on="OK" data-off="NG"></td>
                    <td><label id="lbl6b"></label></td>
                </tr>
                <tr>
                    <td><label id="lbl7a"></label></td>
                    <td><input type="checkbox" id="chk7" data-toggle="toggle" data-width="80" data-on="OK" data-off="NG"></td>
                    <td><label id="lbl7b"></label></td>
                </tr>
            </table>

        </div>
    </body>
</html>
```

```javascript:main.js

var toEnglish = function() {
    Cookies.set('isEnglish', true, {expires: 30});
    location.reload();
};

var toJapanese = function() {
    Cookies.set('isEnglish', false, {expires: 30});
    location.reload();
};

$(function() {
    
    var isEnglish = Cookies.get('isEnglish');
    if (isEnglish == undefined || isEnglish == 'false') {
        isEnglish = false;
    } else {
        isEnglish = true;
    }

    if (isEnglish) {
        document.title = "Closed door confirmation";
        document.getElementById('title').textContent = "Closed door confirmation";
        document.getElementById('description').textContent = "On / Off data is stored and displayed as browser cookies (30 days). Please switch it manually.";
        document.getElementById('normal').textContent = "Normal:";
        document.getElementById('vacation').textContent = "Long vacation:";
    }

    var zeroFormat = function(x) {
        return x.toString().padStart(2, '0');
    };

    var weeks = ['(日)', '(月)', '(火)', '(水)', '(木)', '(金)', '(土)'];
    if (isEnglish) {
        weeks = ['(Sun)', '(Mon)', '(Tue)', '(Wed)', '(Thu)', '(Fri)', '(Sut)'];
    }

    var dayOfWeek = function(x) {
        return weeks[x];
    };

    var items = ['エアコンの停止', '部屋の電気', '窓の鍵閉め', '玄関の鍵閉め', '電気のブレーカー', 'ガスの元栓', '水道の元栓'];
    if (isEnglish) {
        items = ['Air conditioner stop', 'Room electricity', 'Window lock', 'Lock the entrance', 'Electric breaker', 'Gas tap', 'Water tap'];
    }

    for (var i = 0; i < items.length; i++) {

        // 項目名
        var index = (i + 1).toString();
        $('#lbl' + index + 'a').text(items[i]);

        // チェック状態
        var checked = Cookies.get('chk' + index);
        if (checked == undefined || checked == 'false') {
            checked = false;
        } else {
            checked = true;
        }
        
        $('#chk' + index).prop('checked', checked).change();

        // 更新日
        var updatedAt = Cookies.get('lbl' + index + 'b');
        if (updatedAt == 'undefined') {
            updatedAt = '　　　　　　　　';
        }

        $('#lbl' + index + 'b').text(updatedAt);
        $('#lbl' + index + 'b').css('color', 'gray');

        // イベントの購読。On の場合、更新日時を表示する
        $('#chk' + index).change(function() {
            var now = new Date();
            var d = '' + now.getFullYear() + '/' +
                zeroFormat((now.getMonth() + 1)) + '/' +
                zeroFormat(now.getDate()) + ' ' +
                dayOfWeek(now.getDay()) + ' ' +
                zeroFormat(now.getHours()) + ':' +
                zeroFormat(now.getMinutes()) + ':' +
                zeroFormat(now.getSeconds());

            // コントロール名からナンバーのみ抽出して、対応するラベルにセット
            // （chk1 から 1 のような）
            var index = $(this).prop('id').substr(-1, 1);
            $('#lbl' + index + 'b').text(d);

            // 切り替えるたびに、クッキーに保存
            Cookies.set('chk' + index, $(this).prop('checked'), {expires: 30});
            Cookies.set('lbl' + index + 'b', d, {expires: 30});
        });
        
    }
});
```

## 最後に

心配性な方向けのもの（というか自分用というか）なのですが、同じような境遇の方の役に立てれば嬉しいです。最後まで読んでいただき、ありがとうございました。