diff --git a/.gitignore b/.gitignore index ec7ded8..68785a8 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ npm-debug.log .idea/ *.iml /tmp/ + +*.swp diff --git a/assets/css/app.scss b/assets/css/app.scss index 843e5d1..7eeff1a 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -118,3 +118,10 @@ 0% { opacity: 1; } 100% { opacity: 0; } } + +.loading-container { + margin: 20px; + width: 100px; + height: 100px; + position: relative; +} diff --git a/assets/js/app.js b/assets/js/app.js index 37936fc..7120435 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,6 +1,6 @@ // We import the CSS which is extracted to its own file by esbuild. // Remove this line if you add a your own CSS build pipeline (e.g postcss). -import "../css/app.scss" +import "../css/app.scss"; // If you want to use Phoenix channels, run `mix help phx.gen.channel` // to get started and then uncomment the line below. @@ -20,26 +20,68 @@ import "../css/app.scss" // // Include phoenix_html to handle method=PUT/DELETE in forms and buttons. -import "phoenix_html" +import "phoenix_html"; // Establish Phoenix Socket and LiveView configuration. -import {Socket} from "phoenix" -import {LiveSocket} from "phoenix_live_view" -import topbar from "../vendor/topbar" +import { Socket } from "phoenix"; +import { LiveSocket } from "phoenix_live_view"; +import topbar from "../vendor/topbar"; +import ProgressBar from "progressbar.js"; -let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") -let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}}) +let csrfToken = document + .querySelector("meta[name='csrf-token']") + .getAttribute("content"); +let liveSocket = new LiveSocket("/live", Socket, { + params: { _csrf_token: csrfToken }, +}); // Show progress bar on live navigation and form submits -topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) -window.addEventListener("phx:page-loading-start", info => topbar.show()) -window.addEventListener("phx:page-loading-stop", info => topbar.hide()) +topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" }); +window.addEventListener("phx:page-loading-start", (info) => topbar.show()); +window.addEventListener("phx:page-loading-stop", (info) => topbar.hide()); // connect if there are any LiveViews on the page -liveSocket.connect() +liveSocket.connect(); // expose liveSocket on window for web console debug logs and latency simulation: // >> liveSocket.enableDebug() // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session // >> liveSocket.disableLatencySim() -window.liveSocket = liveSocket +window.liveSocket = liveSocket; +// progressbar.js@1.0.0 version is used +// Docs: http://progressbarjs.readthedocs.org/en/1.0.0/ + +window.loadStatusProgressBar = function(divId, isDown) { + console.log(`Loading bar for id ${divId}`); + let bar = new ProgressBar.Circle(`#${divId}`, { + color: "#aaa", + // This has to be the same size as the maximum width to + // prevent clipping + strokeWidth: 4, + trailWidth: 1, + easing: "easeInOut", + duration: 1400, + text: { + autoStyleContainer: false, + }, + from: { color: "#aaa", width: 1 }, + to: { color: isDown ? "#d65c3a" : "#5cb571", width: 4 }, // #333 + + // Set default step function for all animate calls + step: function (state, circle) { + circle.path.setAttribute("stroke", state.color); + circle.path.setAttribute("stroke-width", state.width); + + var value = Math.round(circle.value() * 100); + if (value === 0) { + circle.setText(""); + } else { + circle.setText(value); + } + }, + }); + bar.text.style.fontFamily = '"Raleway", Helvetica, sans-serif'; + bar.text.style.fontSize = "2rem"; + + bar.animate(1.0); // Number from 0.0 to 1.0 +} diff --git a/assets/package-lock.json b/assets/package-lock.json index 6a86cb2..cc3c032 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -9,7 +9,8 @@ "font-awesome": "^4.7.0", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", - "phoenix_live_view": "file:../deps/phoenix_live_view" + "phoenix_live_view": "file:../deps/phoenix_live_view", + "progressbar.js": "^1.1.0" }, "devDependencies": { "esbuild": "^0.14.42", @@ -482,7 +483,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -585,6 +585,14 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/progressbar.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/progressbar.js/-/progressbar.js-1.1.0.tgz", + "integrity": "sha512-K68/xcyXKo2I6T3PfIkXrRaycxROmWeU4bugb49iulWR25cU94PM0cfZ47S0jDhG5K3vPhZwCOy1fgb5Pgh6UQ==", + "dependencies": { + "shifty": "^2.1.2" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -614,6 +622,14 @@ "node": ">=12.0.0" } }, + "node_modules/shifty": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/shifty/-/shifty-2.17.1.tgz", + "integrity": "sha512-ime0aar6UX4OHG8WjC84ThqLU6PWf/6UdSLCMJFvWmeYx1PmuxvRNwiJom4py7GVLv10xRfDKt0MssotIxkYhQ==", + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -879,7 +895,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "optional": true }, "glob-parent": { @@ -948,6 +963,14 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "progressbar.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/progressbar.js/-/progressbar.js-1.1.0.tgz", + "integrity": "sha512-K68/xcyXKo2I6T3PfIkXrRaycxROmWeU4bugb49iulWR25cU94PM0cfZ47S0jDhG5K3vPhZwCOy1fgb5Pgh6UQ==", + "requires": { + "shifty": "^2.1.2" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -968,6 +991,14 @@ "source-map-js": ">=0.6.2 <2.0.0" } }, + "shifty": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/shifty/-/shifty-2.17.1.tgz", + "integrity": "sha512-ime0aar6UX4OHG8WjC84ThqLU6PWf/6UdSLCMJFvWmeYx1PmuxvRNwiJom4py7GVLv10xRfDKt0MssotIxkYhQ==", + "requires": { + "fsevents": "^2.3.2" + } + }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", diff --git a/assets/package.json b/assets/package.json index fcfb832..f3242fc 100644 --- a/assets/package.json +++ b/assets/package.json @@ -8,6 +8,7 @@ "font-awesome": "^4.7.0", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", - "phoenix_live_view": "file:../deps/phoenix_live_view" + "phoenix_live_view": "file:../deps/phoenix_live_view", + "progressbar.js": "^1.1.0" } } diff --git a/lib/zout_web/templates/layout/root.html.heex b/lib/zout_web/templates/layout/root.html.heex index 082e44e..2482765 100644 --- a/lib/zout_web/templates/layout/root.html.heex +++ b/lib/zout_web/templates/layout/root.html.heex @@ -6,6 +6,7 @@ <%= live_title_tag assigns[:page_title] || "Zout", suffix: " ยท Phoenix Framework" %> + diff --git a/lib/zout_web/templates/project/index.html.heex b/lib/zout_web/templates/project/index.html.heex index 77bb1f1..e9fff72 100644 --- a/lib/zout_web/templates/project/index.html.heex +++ b/lib/zout_web/templates/project/index.html.heex @@ -5,6 +5,7 @@ Project Status + Uptime @@ -14,7 +15,13 @@ <%= render_status(downtime) %> +
+ <% end %> diff --git a/lib/zout_web/views/project_view.ex b/lib/zout_web/views/project_view.ex index 317c07b..3005fea 100644 --- a/lib/zout_web/views/project_view.ex +++ b/lib/zout_web/views/project_view.ex @@ -8,4 +8,8 @@ defmodule ZoutWeb.ProjectView do def render_status(%Downtime{status: status, start: start}), do: "#{status} since #{DateTime.to_iso8601(start)}" + + def is_down(nil), do: false + def is_down(%Downtime{status: :working}), do: false + def is_down(_), do: true end