Changed heartbeats component and simplified HTML structure
This commit is contained in:
parent
a338deefae
commit
1523136573
|
@ -2,16 +2,32 @@
|
|||
|
||||
require_once("../vendor/autoload.php");
|
||||
|
||||
function timediffmin() {
|
||||
return new \Twig\TwigFilter('timediffmin', function ($ago, $now) {
|
||||
return round((strtotime($now) - strtotime($ago)) / 60);
|
||||
});
|
||||
function globalstatus() {
|
||||
return new \Twig\TwigFilter('globalstatus', function (int $online, int $total) {
|
||||
if ($online == $total) return 1;
|
||||
elseif ($online == 0) return 0;
|
||||
else return 2;
|
||||
});
|
||||
}
|
||||
|
||||
function isof() {
|
||||
return new \Twig\TwigFilter('isof', function ($online, $total) {
|
||||
if($online == $total) return "all";
|
||||
elseif($online == 0) return "none";
|
||||
else return "some";
|
||||
function globalstatustext() {
|
||||
return new \Twig\TwigFilter('globalstatustext', function (int $status) {
|
||||
$msgs = [
|
||||
"none",
|
||||
"all",
|
||||
"some"
|
||||
];
|
||||
return "header.title.$msgs[$status]";
|
||||
});
|
||||
}
|
||||
|
||||
function statusicon() {
|
||||
return new \Twig\TwigFilter('statusicon', function (int $status) {
|
||||
$icons = [
|
||||
0 => "error",
|
||||
1 => "success",
|
||||
2 => "warning"
|
||||
];
|
||||
return "/icon/{$icons[$status]}.svg";
|
||||
});
|
||||
}
|
|
@ -34,8 +34,9 @@ class UptimeStatus {
|
|||
$loader = new \Twig\Loader\FilesystemLoader("../view/");
|
||||
$twig = new \Twig\Environment($loader, $twig_config);
|
||||
|
||||
$twig->addFilter(\Filters\timediffmin());
|
||||
$twig->addFilter(\Filters\isof());
|
||||
$twig->addFilter(\Filters\globalstatus());
|
||||
$twig->addFilter(\Filters\globalstatustext());
|
||||
$twig->addFilter(\Filters\statusicon());
|
||||
|
||||
$locale = new \Locale\Locale($this->cfg("default_language"));
|
||||
$twig->addFilter($locale->t());
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
"header.title.some": "Některé služby neběží",
|
||||
"header.title.none": "Všechny služby jsou offline",
|
||||
"header.updated": "Poslední aktualizace {{date}}.",
|
||||
"heartbeats.now": "nyní",
|
||||
"heartbeats.ago": "před {{minutes}} minutami",
|
||||
"monitor.uptime": "<b>{{pct}}%</b> uptime",
|
||||
"status.1": "V pořádku",
|
||||
"status.0": "Výpadek",
|
||||
"status.2": "Problém",
|
||||
"dateformat": "j. m. Y G:i",
|
||||
"footer.poweredby": "Poháněno projektem <b><a href=\"{{link}}\" target=\"_blank\">Uptime Status</a></b>"
|
||||
}
|
|
@ -3,9 +3,10 @@
|
|||
"header.title.some": "Some services are down",
|
||||
"header.title.none": "All services are down",
|
||||
"header.updated": "Last updated on {{date}}.",
|
||||
"heartbeats.now": "now",
|
||||
"heartbeats.ago": "{{minutes}} minutes ago",
|
||||
"monitor.uptime": "<b>{{pct}}%</b> uptime",
|
||||
"status.1": "Operational",
|
||||
"status.0": "Downtime",
|
||||
"status.2": "Problem",
|
||||
"dateformat": "M j, Y, g:i a",
|
||||
"footer.poweredby": "Powered by <b><a href=\"{{link}}\" target=\"_blank\">Uptime Status</a></b>"
|
||||
}
|
1
public/icon/error.svg
Normal file
1
public/icon/error.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1.75 1.75 20.5 20.5"><path fill="#F87171" d="m8.4 17l3.6-3.6l3.6 3.6l1.4-1.4l-3.6-3.6L17 8.4L15.6 7L12 10.6L8.4 7L7 8.4l3.6 3.6L7 15.6L8.4 17Zm3.6 5q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22Z"/></svg>
|
After Width: | Height: | Size: 437 B |
1
public/icon/success.svg
Normal file
1
public/icon/success.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1.75 1.75 20.5 20.5"><path fill="#10B981" d="m10.6 16.6l7.05-7.05l-1.4-1.4l-5.65 5.65l-2.85-2.85l-1.4 1.4l4.25 4.25ZM12 22q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22Z"/></svg>
|
After Width: | Height: | Size: 410 B |
1
public/icon/warning.svg
Normal file
1
public/icon/warning.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1.75 1.75 20.5 20.5"><path fill="#FFBB6D" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10Zm-1-7v2h2v-2h-2Zm0-8v6h2V7h-2Z"/></svg>
|
After Width: | Height: | Size: 207 B |
148
public/style.css
148
public/style.css
|
@ -2,12 +2,12 @@
|
|||
|
||||
--border-radius: .35rem;
|
||||
|
||||
--title-color: #fff;
|
||||
--text-color: #fff9;
|
||||
--bg-color: #0f121a;
|
||||
--title-color: 255, 255, 255;
|
||||
--text-color: 177, 177, 177;
|
||||
--bg-color: 15, 18, 26;
|
||||
|
||||
--card-bg-color: #23273191;
|
||||
--card-border-color: #21242d;
|
||||
--card-bg-color: 35, 39, 49;
|
||||
--card-border-color: 33, 36, 45;
|
||||
|
||||
--green-color: 16, 185, 129;
|
||||
--red-color: 248, 113, 113;
|
||||
|
@ -16,11 +16,12 @@
|
|||
}
|
||||
|
||||
body {
|
||||
color: var(--text-color);
|
||||
background-color: var(--bg-color);
|
||||
color: rgb(var(--text-color));
|
||||
background-color: rgb(var(--bg-color));
|
||||
font-family: Cantarell, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
line-height: 1.15;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
|
@ -34,7 +35,7 @@ body {
|
|||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
color: var(--title-color);
|
||||
color: rgb(var(--title-color));
|
||||
}
|
||||
|
||||
h3, h4 {
|
||||
|
@ -42,11 +43,11 @@ h3, h4 {
|
|||
}
|
||||
|
||||
a {
|
||||
color: var(--text-color);
|
||||
color: rgb(var(--text-color));
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--title-color);
|
||||
color: rgb(var(--title-color));
|
||||
}
|
||||
|
||||
|
||||
|
@ -71,18 +72,6 @@ header::after {
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
header.all {
|
||||
--color: var(--green-color);
|
||||
}
|
||||
|
||||
header.some {
|
||||
--color: var(--yellow-color);
|
||||
}
|
||||
|
||||
header.none {
|
||||
--color: var(--red-color);
|
||||
}
|
||||
|
||||
header .icon {
|
||||
color: rgb(var(--color));
|
||||
border-radius: 50%;
|
||||
|
@ -92,10 +81,7 @@ header .icon {
|
|||
position: relative;
|
||||
display: flex;
|
||||
background-image: linear-gradient(-45deg, rgb(var(--color), .1) 0%, rgb(var(--color), .5) 100%);
|
||||
}
|
||||
|
||||
header .icon svg {
|
||||
filter: drop-shadow(0 0 10px var(--bg-color));
|
||||
filter: drop-shadow(0 0 10px rgb(var(--bg-color)));
|
||||
}
|
||||
|
||||
header p {
|
||||
|
@ -113,27 +99,23 @@ footer {
|
|||
/* GROUP */
|
||||
|
||||
.group {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
background: rgb(var(--card-bg-color), .5);
|
||||
border: 1px solid rgb(var(--card-border-color));
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.group .item {
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.group .item > .inner {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.group .item:hover {
|
||||
background-color: #fff1;
|
||||
}
|
||||
|
||||
.group > .header > .inner {
|
||||
.group > .header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
@ -148,9 +130,6 @@ footer {
|
|||
|
||||
.monitor {
|
||||
--color: 150, 150, 150;
|
||||
}
|
||||
|
||||
.monitor > .inner {
|
||||
display: flex;
|
||||
gap: 7px;
|
||||
flex-direction: column;
|
||||
|
@ -178,50 +157,97 @@ footer {
|
|||
|
||||
.heartbeats {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.heartbeats .items {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
gap: 0px;
|
||||
height: 5px;
|
||||
transition: all .3s .5s;
|
||||
opacity: .4;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.heartbeats .items > * {
|
||||
.heartbeats .tooltip {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background-color: rgb(var(--card-bg-color));
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid rgb(var(--card-border-color));
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-top: 5px;
|
||||
margin-left: -90px;
|
||||
width: 180px;
|
||||
box-shadow: 0 0 10px 0 #0005;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.heartbeats .tooltip > * {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.heartbeats .tooltip .status {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.heartbeats .tooltip .status :nth-child(1) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 7px;
|
||||
font-weight: 700;
|
||||
color: rgb(var(--title-color));
|
||||
}
|
||||
|
||||
.heartbeats .tooltip img {
|
||||
max-width: 19px;
|
||||
}
|
||||
|
||||
.heartbeats .tooltip .date {
|
||||
font-size: .9rem;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
background-color: #fff1;
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
.heartbeats .beat {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
background-color: rgb(var(--color));
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.heartbeats .time {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: .9rem;
|
||||
opacity: .7;
|
||||
line-height: 1;
|
||||
.heartbeats .beat:last-child {
|
||||
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||
}
|
||||
|
||||
.monitor:hover .heartbeats .items {
|
||||
height: 25px;
|
||||
.heartbeats .beat:first-child {
|
||||
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
||||
}
|
||||
|
||||
.heartbeats .beat:hover {
|
||||
box-shadow: inset 0 0 0 2px rgb(var(--title-color));
|
||||
}
|
||||
|
||||
.heartbeats .beat:hover .tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.monitor:hover .heartbeats {
|
||||
opacity: 1;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
|
||||
/* STATUS COLORS */
|
||||
|
||||
.monitor.status-1, .monitor .heartbeats .status-1 {
|
||||
.status-1 {
|
||||
--color: var(--green-color);
|
||||
}
|
||||
|
||||
.monitor.status-0, .monitor .heartbeats .status-0 {
|
||||
.status-0 {
|
||||
--color: var(--red-color);
|
||||
}
|
||||
|
||||
.monitor.status-2, .monitor .heartbeats .status-2 {
|
||||
.status-2 {
|
||||
--color: var(--yellow-color);
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
<div class="group">
|
||||
<div class="header item">
|
||||
<div class="inner">
|
||||
<h3>{{ group.name }}</h3>
|
||||
<div class="status">{{ group.online }} / {{ group.monitors | length }}</div>
|
||||
</div>
|
||||
<h3>{{ group.name }}</h3>
|
||||
<div class="status">{{ group.online }} / {{ group.monitors | length }}</div>
|
||||
</div>
|
||||
{% for monitor in group.monitors %}
|
||||
{{ include("./monitor.twig") }}
|
||||
|
|
|
@ -1,23 +1,8 @@
|
|||
<header class="{{ online | isof(total) }}">
|
||||
{% set gs = online | globalstatus(total) %}
|
||||
<header class="status-{{ gs }}">
|
||||
<div class="inner">
|
||||
{% if online == total %}
|
||||
|
||||
<div class="icon">{{ include("./icon/success.svg") }}</div>
|
||||
<h1>{{ "header.title.all" | t }}</h1>
|
||||
|
||||
{% elseif online == 0 %}
|
||||
|
||||
<div class="icon">{{ include("./icon/error.svg") }}</div>
|
||||
<h1>{{ "header.title.none" | t }}</h1>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="icon">{{ include("./icon/warning.svg") }}</div>
|
||||
<h1>{{ "header.title.some" | t }}</h1>
|
||||
|
||||
{% endif %}
|
||||
<p>
|
||||
{{ "header.updated" | t({date: now | date}) }}
|
||||
</p>
|
||||
<img class="icon" src="{{ gs | statusicon }}" />
|
||||
<h1>{{ gs | globalstatustext | t }}</h1>
|
||||
<p>{{ "header.updated" | t({date: now | date}) }}</p>
|
||||
</div>
|
||||
</header>
|
|
@ -1,11 +1,24 @@
|
|||
<div class="heartbeats">
|
||||
<div class="items">
|
||||
{% for heartbeat in monitor.heartbeats %}
|
||||
<div class="status-{{ heartbeat.status }}"></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="time">
|
||||
<div>{{ "heartbeats.ago" | t({minutes: (monitor.heartbeats | first).time | timediffmin((monitor.heartbeats | last).time)}) }}</div>
|
||||
<div>{{ "heartbeats.now" | t }}</div>
|
||||
</div>
|
||||
{% for heartbeat in monitor.heartbeats %}
|
||||
<div class="beat status-{{ heartbeat.status }}">
|
||||
<div class="tooltip">
|
||||
<div class="status">
|
||||
<div>
|
||||
<img src="{{ heartbeat.status | statusicon }}" />
|
||||
<span>
|
||||
{{ ("status." ~ heartbeat.status) | t }}
|
||||
</span>
|
||||
</div>
|
||||
{% if heartbeat.ping %}
|
||||
<div>
|
||||
Ping: {{ heartbeat.ping }}ms
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="date">
|
||||
{{ heartbeat.time | date }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1.75 1.75 20.5 20.5"><path fill="currentColor" d="m8.4 17l3.6-3.6l3.6 3.6l1.4-1.4l-3.6-3.6L17 8.4L15.6 7L12 10.6L8.4 7L7 8.4l3.6 3.6L7 15.6L8.4 17Zm3.6 5q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22Z"/></svg>
|
Before Width: | Height: | Size: 442 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1.75 1.75 20.5 20.5"><path fill="currentColor" d="m10.6 16.6l7.05-7.05l-1.4-1.4l-5.65 5.65l-2.85-2.85l-1.4 1.4l4.25 4.25ZM12 22q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22Z"/></svg>
|
Before Width: | Height: | Size: 415 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="1.75 1.75 20.5 20.5"><path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10Zm-1-7v2h2v-2h-2Zm0-8v6h2V7h-2Z"/></svg>
|
Before Width: | Height: | Size: 212 B |
|
@ -1,25 +1,15 @@
|
|||
{% set status = monitor.last.status %}
|
||||
<div class="item monitor status-{{ status }}">
|
||||
<div class="inner">
|
||||
<div class="header">
|
||||
<div class="icon">
|
||||
{% if status == 0 %}
|
||||
{{ include("./icon/error.svg") }}
|
||||
{% elseif status == 1 %}
|
||||
{{ include("./icon/success.svg") }}
|
||||
{% elseif status == 2 %}
|
||||
{{ include("./icon/warning.svg") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<h4>
|
||||
{{ monitor.name }}
|
||||
</h4>
|
||||
<div class="uptime">
|
||||
{{ "monitor.uptime" | t({pct: (monitor.uptime * 100) | number_format(1, '.')}) | raw }}
|
||||
</div>
|
||||
<div class="header">
|
||||
<img class="icon" src="{{ status | statusicon }}" />
|
||||
<h4>
|
||||
{{ monitor.name }}
|
||||
</h4>
|
||||
<div class="uptime">
|
||||
{{ "monitor.uptime" | t({pct: (monitor.uptime * 100) | number_format(1, '.')}) | raw }}
|
||||
</div>
|
||||
{% if monitor.opt.rich %}
|
||||
{{ include("./heartbeats.twig") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if monitor.opt.rich %}
|
||||
{{ include("./heartbeats.twig") }}
|
||||
{% endif %}
|
||||
</div>
|
Loading…
Reference in a new issue