diff --git a/README.md b/README.md
new file mode 100644
index 000000000..34948fe59
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+# Uptime Kuma
+
+# Features
+
+* Monitoring uptime for HTTP(s) / TCP / Ping.
+* Fancy, Reactive, Fast UI/UX.
+* Notifications via Webhook, Telegram, Discord and email (SMTP).
+* 20 seconds interval.
+
+# How to Use
+
+npm
+
+Docker
+
+One-click Deploy to DigitalOcean
+
+# Motivation
+
+* I was looking for a self-hosted monitoring tool like "Uptime Robot", but it is hard to find a suitable one.
+* Want to build a fancy UI.
+* Learn Vue 3 and vite.js.
+* Show the power of Bootstrap 5. 
+* Try to use WebSocket with SPA instead of REST API.
+* Deploy my first Docker image to Docker Hub.
+
+
+If you love this project, please consider giving me a ⭐.
+
diff --git a/server/model/monitor.js b/server/model/monitor.js
index 8346040bc..3b19d2ff1 100644
--- a/server/model/monitor.js
+++ b/server/model/monitor.js
@@ -5,7 +5,7 @@ var timezone = require('dayjs/plugin/timezone')
 dayjs.extend(utc)
 dayjs.extend(timezone)
 const axios = require("axios");
-const {tcping} = require("../util-server");
+const {tcping, ping} = require("../util-server");
 const {R} = require("redbean-node");
 const {BeanModel} = require("redbean-node/dist/bean-model");
 
@@ -67,6 +67,10 @@ class Monitor extends BeanModel {
                 } else if (this.type === "port") {
                     bean.ping = await tcping(this.hostname, this.port);
                     bean.status = 1;
+
+                } else if (this.type === "ping") {
+                    bean.ping = await ping(this.hostname);
+                    bean.status = 1;
                 }
 
             } catch (error) {
@@ -125,19 +129,49 @@ class Monitor extends BeanModel {
      * @param duration : int Hours
      */
     static async sendUptime(duration, io, monitorID, userID) {
-        let downtime = parseInt(await R.getCell(`
-            SELECT SUM(duration)
+        let sec = duration * 3600;
+
+        let downtimeList = await R.getAll(`
+            SELECT duration, time
             FROM heartbeat
             WHERE time > DATE('now', ? || ' hours')
             AND status = 0
             AND monitor_id = ? `, [
             -duration,
             monitorID
-        ]));
+        ]);
+
+        let downtime = 0;
+
+        for (let row of downtimeList) {
+            let value = parseInt(row.duration)
+            let time = row.time
+
+            // Handle if heartbeat duration longer than the target duration
+            // e.g.   Heartbeat duration = 28hrs, but target duration = 24hrs
+            if (value <= sec) {
+                downtime += value;
+            } else {
+                console.log("Now: " + dayjs.utc());
+                console.log("Time: " + dayjs(time))
+
+                let trim = dayjs.utc().diff(dayjs(time), 'second');
+                console.log("trim: " + trim);
+                value = sec - trim;
+
+                if (value < 0) {
+                    value = 0;
+                }
+                downtime += value;
+            }
+        }
 
-        let sec = duration * 3600;
         let uptime = (sec - downtime) / sec;
 
+        if (uptime < 0) {
+            uptime = 0;
+        }
+
         io.to(userID).emit("uptime", monitorID, duration, uptime);
     }
 }
diff --git a/server/ping-lite.js b/server/ping-lite.js
new file mode 100644
index 000000000..e290d8876
--- /dev/null
+++ b/server/ping-lite.js
@@ -0,0 +1,118 @@
+// https://github.com/ben-bradley/ping-lite/blob/master/ping-lite.js
+// Fixed on Windows
+
+var spawn = require('child_process').spawn,
+    events = require('events'),
+    fs = require('fs'),
+    WIN = /^win/.test(process.platform),
+    LIN = /^linux/.test(process.platform),
+    MAC = /^darwin/.test(process.platform);
+
+module.exports = Ping;
+
+function Ping(host, options) {
+    if (!host)
+        throw new Error('You must specify a host to ping!');
+
+    this._host = host;
+    this._options = options = (options || {});
+
+    events.EventEmitter.call(this);
+
+    if (WIN) {
+        this._bin = 'c:/windows/system32/ping.exe';
+        this._args = (options.args) ? options.args : [ '-n', '1', '-w', '5000', host ];
+        this._regmatch = /[><=]([0-9.]+?)ms/;
+    }
+    else if (LIN) {
+        this._bin = '/bin/ping';
+        this._args = (options.args) ? options.args : [ '-n', '-w', '2', '-c', '1', host ];
+        this._regmatch = /=([0-9.]+?) ms/; // need to verify this
+    }
+    else if (MAC) {
+        this._bin = '/sbin/ping';
+        this._args = (options.args) ? options.args : [ '-n', '-t', '2', '-c', '1', host ];
+        this._regmatch = /=([0-9.]+?) ms/;
+    }
+    else {
+        throw new Error('Could not detect your ping binary.');
+    }
+
+    if (!fs.existsSync(this._bin))
+        throw new Error('Could not detect '+this._bin+' on your system');
+
+    this._i = 0;
+
+    return this;
+}
+
+Ping.prototype.__proto__ = events.EventEmitter.prototype;
+
+// SEND A PING
+// ===========
+Ping.prototype.send = function(callback) {
+    var self = this;
+    callback = callback || function(err, ms) {
+        if (err) return self.emit('error', err);
+        else     return self.emit('result', ms);
+    };
+
+    var _ended, _exited, _errored;
+
+    this._ping = spawn(this._bin, this._args); // spawn the binary
+
+    this._ping.on('error', function(err) { // handle binary errors
+        _errored = true;
+        callback(err);
+    });
+
+    this._ping.stdout.on('data', function(data) { // log stdout
+        this._stdout = (this._stdout || '') + data;
+    });
+
+    this._ping.stdout.on('end', function() {
+        _ended = true;
+        if (_exited && !_errored) onEnd.call(self._ping);
+    });
+
+    this._ping.stderr.on('data', function(data) { // log stderr
+        this._stderr = (this._stderr || '') + data;
+    });
+
+    this._ping.on('exit', function(code) { // handle complete
+        _exited = true;
+        if (_ended && !_errored) onEnd.call(self._ping);
+    });
+
+    function onEnd() {
+        var stdout = this.stdout._stdout,
+            stderr = this.stderr._stderr,
+            ms;
+
+        if (stderr)
+            return callback(new Error(stderr));
+        else if (!stdout)
+            return callback(new Error('No stdout detected'));
+
+        ms = stdout.match(self._regmatch); // parse out the ##ms response
+        ms = (ms && ms[1]) ? Number(ms[1]) : ms;
+
+        callback(null, ms);
+    }
+};
+
+// CALL Ping#send(callback) ON A TIMER
+// ===================================
+Ping.prototype.start = function(callback) {
+    var self = this;
+    this._i = setInterval(function() {
+        self.send(callback);
+    }, (self._options.interval || 5000));
+    self.send(callback);
+};
+
+// STOP SENDING PINGS
+// ==================
+Ping.prototype.stop = function() {
+    clearInterval(this._i);
+};
diff --git a/server/util-server.js b/server/util-server.js
index 8e03284a1..ca8e9f8ee 100644
--- a/server/util-server.js
+++ b/server/util-server.js
@@ -1,4 +1,5 @@
 const tcpp = require('tcp-ping');
+const Ping = require("./ping-lite");
 
 exports.tcping = function (hostname, port) {
     return new Promise((resolve, reject) => {
@@ -20,3 +21,19 @@ exports.tcping = function (hostname, port) {
         });
     });
 }
+
+exports.ping = function (hostname) {
+    return new Promise((resolve, reject) => {
+        const ping = new Ping(hostname);
+
+        ping.send(function(err, ms) {
+            if (err) {
+                reject(err)
+            } else if (ms === null) {
+                reject(new Error("timeout"))
+            } else {
+                resolve(Math.round(ms))
+            }
+        });
+    });
+}
diff --git a/src/components/CountUp.vue b/src/components/CountUp.vue
index 5cfc8d4c6..b929e52eb 100644
--- a/src/components/CountUp.vue
+++ b/src/components/CountUp.vue
@@ -48,7 +48,7 @@ export default {
             let frames = 12;
             let step = Math.floor(diff / frames);
 
-            if (! this.isNum || (diff > 0 && step < 1) || (diff < 0 && step > 1) || diff === 0) {
+            if (isNaN(step) || ! this.isNum || (diff > 0 && step < 1) || (diff < 0 && step > 1) || diff === 0) {
                 // Lazy to NOT this condition, hahaha.
             } else {
                 for (let i = 1; i < frames; i++) {
diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue
index f752b8f31..ad8114fcb 100644
--- a/src/components/Uptime.vue
+++ b/src/components/Uptime.vue
@@ -18,7 +18,7 @@ export default {
 
             let key = this.monitor.id + "_" + this.type;
 
-            if (this.$root.uptimeList[key]) {
+            if (this.$root.uptimeList[key] !== undefined) {
                 return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%";
             } else {
                 return "N/A"
diff --git a/src/pages/DashboardHome.vue b/src/pages/DashboardHome.vue
index 396afdd05..27c8adf38 100644
--- a/src/pages/DashboardHome.vue
+++ b/src/pages/DashboardHome.vue
@@ -5,60 +5,6 @@
 
         <div class="shadow-box big-padding text-center">
             <div class="row">
-
-                <div class="col-12">
-                    <div class="hp-bar-big">
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                        <div></div>
-                    </div>
-                </div>
-
             	<div class="col">
                     <h3>Up</h3>
                     <span class="num">{{ stats.up }}</span>
@@ -76,32 +22,43 @@
                     <span class="num text-secondary">{{ stats.pause }}</span>
                 </div>
             </div>
+            <div class="row" v-if="false">
+                <div class="col-3">
+                    <h3>Uptime</h3>
+                    <p>(24-hour)</p>
+                    <span class="num"></span>
+                </div>
+                <div class="col-3">
+                    <h3>Uptime</h3>
+                    <p>(30-day)</p>
+                    <span class="num"></span>
+                </div>
+            </div>
         </div>
 
-        <div class="row mt-4">
-        	<div class="col-8">
-                <h4>Latest Incident</h4>
+        <div class="shadow-box" style="margin-top: 25px;">
+            <table class="table table-borderless table-hover">
+                <thead>
+                <tr>
+                    <th>Name</th>
+                    <th>Status</th>
+                    <th>DateTime</th>
+                    <th>Message</th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr v-for="beat in importantHeartBeatList">
+                    <td>{{ beat.name }}</td>
+                    <td><Status :status="beat.status" /></td>
+                    <td><Datetime :value="beat.time" /></td>
+                    <td>{{ beat.msg }}</td>
+                </tr>
 
-                <div class="shadow-box bg-danger text-light">
-                    MySQL was down.
-                </div>
-
-                <div class="shadow-box bg-primary text-light">
-                    No issues was found.
-                </div>
-
-            </div>
-        	<div class="col-4">
-
-                <h4>Overall Uptime</h4>
-
-                <div class="shadow-box">
-                    <div>100.00% (24 hours)</div>
-                    <div>100.00% (7 days)</div>
-                    <div>100.00% (30 days)</div>
-                </div>
-
-            </div>
+                <tr v-if="importantHeartBeatList.length === 0">
+                    <td colspan="4">No important events</td>
+                </tr>
+                </tbody>
+            </table>
         </div>
     </div>
 
@@ -109,7 +66,10 @@
 </template>
 
 <script>
+import Status from "../components/Status.vue";
+import Datetime from "../components/Datetime.vue";
 export default {
+    components: {Datetime, Status},
     computed: {
         stats() {
             let result = {
@@ -140,6 +100,25 @@ export default {
 
             return result;
         },
+
+        importantHeartBeatList() {
+            let result = [];
+
+            for (let monitorID in this.$root.importantHeartbeatList) {
+                let list =  this.$root.importantHeartbeatList[monitorID]
+                result = result.concat(list);
+            }
+
+            for (let beat of result) {
+                let monitor = this.$root.monitorList[beat.monitorID];
+
+                if (monitor) {
+                    beat.name = monitor.name
+                }
+            }
+
+            return result;
+        }
     }
 }
 </script>
@@ -151,5 +130,18 @@ export default {
     font-size: 30px;
     color: $primary;
     font-weight: bold;
+    display: block;
+}
+
+.shadow-box {
+    padding: 20px;
+}
+
+table {
+    font-size: 14px;
+
+    tr {
+        transition: all ease-in-out 0.2ms;
+    }
 }
 </style>
diff --git a/src/pages/Details.vue b/src/pages/Details.vue
index 21bdc611a..764a3f559 100644
--- a/src/pages/Details.vue
+++ b/src/pages/Details.vue
@@ -2,7 +2,8 @@
     <h1> {{ monitor.name }}</h1>
     <p class="url">
         <a :href="monitor.url" target="_blank" v-if="monitor.type === 'http'">{{ monitor.url }}</a>
-        <span v-if="monitor.type === 'port'">{{ monitor.hostname }}:{{ monitor.port }}</span>
+        <span v-if="monitor.type === 'port'">TCP Ping {{ monitor.hostname }}:{{ monitor.port }}</span>
+        <span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
     </p>
 
     <div class="functions">
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue
index 75f7bf5ab..57ab81189 100644
--- a/src/pages/EditMonitor.vue
+++ b/src/pages/EditMonitor.vue
@@ -27,22 +27,19 @@
                         <input type="url" class="form-control" id="url" v-model="monitor.url" pattern="https?://.+" required>
                     </div>
 
-                    <template v-if="monitor.type === 'port' ">
-                        <div class="mb-3">
-                            <label for="hostname" class="form-label">Hostname</label>
-                            <input type="text" class="form-control" id="hostname" v-model="monitor.hostname" required>
-                        </div>
-
-                        <div class="mb-3">
-                            <label for="port" class="form-label">Port</label>
-                            <input type="number" class="form-control" id="port" v-model="monitor.port" required min="0" max="65535">
-                        </div>
-                    </template>
+                    <div class="mb-3" v-if="monitor.type === 'port' || monitor.type === 'ping' ">
+                        <label for="hostname" class="form-label">Hostname</label>
+                        <input type="text" class="form-control" id="hostname" v-model="monitor.hostname" required>
+                    </div>
 
+                    <div class="mb-3" v-if="monitor.type === 'port' ">
+                        <label for="port" class="form-label">Port</label>
+                        <input type="number" class="form-control" id="port" v-model="monitor.port" required min="0" max="65535" step="1">
+                    </div>
 
                     <div class="mb-3">
                         <label for="interval" class="form-label">Heartbeat Interval (Every {{ monitor.interval }} seconds)</label>
-                        <input type="number" class="form-control" id="interval" v-model="monitor.interval" required min="20">
+                        <input type="number" class="form-control" id="interval" v-model="monitor.interval" required min="20" step="1">
                     </div>
 
                     <div>