diff --git a/.changelog/14677.txt b/.changelog/14677.txt
new file mode 100644
index 000000000..e2b6438b2
--- /dev/null
+++ b/.changelog/14677.txt
@@ -0,0 +1,3 @@
+```release-note:improvement
+ui: attach timestamps and a visual indicator on failure to health checks in the Web UI
+```
diff --git a/ui/app/components/service-status-indicator.hbs b/ui/app/components/service-status-indicator.hbs
index 766c55481..66fe4c618 100644
--- a/ui/app/components/service-status-indicator.hbs
+++ b/ui/app/components/service-status-indicator.hbs
@@ -1 +1,10 @@
-
+
+ {{#if (eq @check.Status "failure")}}
+ ×
+ {{/if}}
+
+ {{format-ts @check.Timestamp timeOnly=true}}
+
diff --git a/ui/app/helpers/format-ts.js b/ui/app/helpers/format-ts.js
index 1169efe0f..eab34613c 100644
--- a/ui/app/helpers/format-ts.js
+++ b/ui/app/helpers/format-ts.js
@@ -2,7 +2,11 @@ import moment from 'moment';
import Helper from '@ember/component/helper';
export function formatTs([date], options = {}) {
- const format = options.short ? 'MMM D' : "MMM DD, 'YY HH:mm:ss ZZ";
+ const format = options.short
+ ? 'MMM D'
+ : options.timeOnly
+ ? 'HH:mm:ss'
+ : "MMM DD, 'YY HH:mm:ss ZZ";
return moment(date).format(format);
}
diff --git a/ui/app/styles/components/services.scss b/ui/app/styles/components/services.scss
index a2adc66e7..96d62365d 100644
--- a/ui/app/styles/components/services.scss
+++ b/ui/app/styles/components/services.scss
@@ -95,23 +95,77 @@ table.health-checks {
width: calc(
750px - 3rem - 50px
); //Sidebar width - padding - table padding
- height: 20px;
+ height: 12px;
padding-top: 30px;
margin-top: -20px;
+ padding-bottom: 20px;
+ margin-bottom: -10px;
overflow: hidden;
box-sizing: content-box;
.service-status-indicator {
- width: 8px;
- height: 8px;
+ width: 12px;
+ height: 12px;
display: block;
position: relative;
+ line-height: 10px;
+ text-align: center;
+ color: white;
+ border-radius: 2px;
&.status-success {
background-color: $nomad-green;
- top: 0px;
}
&.status-failure {
background-color: $red;
- top: 12px;
+ }
+
+ & > .timestamp {
+ color: black;
+ font-size: 0.75rem;
+ position: absolute;
+ top: 100%;
+ text-align: center;
+ white-space: nowrap;
+ left: 50%;
+ width: 100px;
+ margin-left: -50px;
+ margin-top: calc(50% + 2px);
+
+ & > span {
+ visibility: hidden;
+ }
+
+ &:before {
+ display: none;
+ border-left: 1px solid $grey-blue;
+ content: '';
+ position: absolute;
+ left: 50%;
+ height: 50%;
+ top: -50%;
+ }
+
+ &:after {
+ display: block;
+ width: calc(12px + 2px); // account for grid.gap
+ border-top: 1px solid $grey-blue;
+ content: '';
+ position: absolute;
+ left: calc(50% - 1px);
+ margin-left: -6px;
+ top: calc(-50% - 1px);
+ }
+ }
+
+ &:nth-child(8n + 1) > .timestamp {
+ &:before {
+ display: block;
+ }
+ & > span {
+ visibility: visible;
+ }
+ & > span {
+ visibility: visible;
+ }
}
}
}