UX: Apply admin table to webhooks (#30317)

* UX: Apply admin table classes for consistent mobile styling on the web hooks page

* DEV: Remove icon on the status component; update status classes

* DEV: Update tests for webhook status component

* DEV: add space var with a smaller value

* DEV: Add styling for different status labels
This commit is contained in:
Ella E. 2024-12-17 08:52:29 -07:00 committed by GitHub
parent e04f535601
commit 37f032752e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 116 additions and 96 deletions

View File

@ -1,10 +1,8 @@
import Component from "@glimmer/component";
import icon from "discourse-common/helpers/d-icon";
import { i18n } from "discourse-i18n";
export default class WebhookStatus extends Component {
iconNames = ["far-circle", "circle-xmark", "circle", "circle"];
iconClasses = ["text-muted", "text-danger", "text-successful", "text-muted"];
statusClasses = ["--inactive", "--critical", "--success", "--inactive"];
get status() {
const lastStatus = this.args.webhook.get("last_delivery_status");
@ -15,16 +13,17 @@ export default class WebhookStatus extends Component {
return i18n(`admin.web_hooks.delivery_status.${this.status.name}`);
}
get iconName() {
return this.iconNames[this.status.id - 1];
}
get iconClass() {
return this.iconClasses[this.status.id - 1];
get statusClass() {
return this.statusClasses[this.status.id - 1];
}
<template>
{{icon this.iconName class=this.iconClass}}
{{this.deliveryStatus}}
<div role="status" class="status-label {{this.statusClass}}">
<div class="status-label-indicator">
</div>
<div class="status-label-text">
{{this.deliveryStatus}}
</div>
</div>
</template>
}

View File

@ -14,19 +14,34 @@
{{#if this.model}}
<LoadMore @selector=".web-hooks tr" @action={{this.loadMore}}>
<table class="web-hooks grid">
<table class="d-admin-table web-hooks">
<thead>
<tr>
<th>{{i18n "admin.web_hooks.delivery_status.title"}}</th>
<th>{{i18n "admin.web_hooks.payload_url"}}</th>
<th>{{i18n "admin.web_hooks.description_label"}}</th>
<th>{{i18n "admin.web_hooks.controls"}}</th>
<th>{{i18n "admin.web_hooks.delivery_status.title"}}</th>
<th></th>
</tr>
</thead>
<tbody>
{{#each this.model as |webhook|}}
<tr>
<td class="delivery-status">
<tr class="d-admin-row__content">
<td class="d-admin-row__overview payload-url">
<LinkTo @route="adminWebHooks.edit" @model={{webhook}}>
{{webhook.payload_url}}
</LinkTo>
</td>
<td class="d-admin-row__detail description">
<div class="d-admin-row__mobile-label">
{{i18n "admin.web_hooks.description_label"}}
</div>
{{webhook.description}}
</td>
<td class="d-admin-row__detail delivery-status">
<div class="d-admin-row__mobile-label">
{{i18n "admin.web_hooks.delivery_status.title"}}
</div>
<LinkTo @route="adminWebHooks.show" @model={{webhook}}>
<WebhookStatus
@deliveryStatuses={{this.deliveryStatuses}}
@ -34,28 +49,24 @@
/>
</LinkTo>
</td>
<td class="payload-url">
<LinkTo @route="adminWebHooks.edit" @model={{webhook}}>
{{webhook.payload_url}}
</LinkTo>
</td>
<td class="description">{{webhook.description}}</td>
<td class="controls">
<LinkTo
@route="adminWebHooks.edit"
@model={{webhook}}
class="btn btn-default no-text"
title={{i18n "admin.web_hooks.edit"}}
>
{{d-icon "far-pen-to-square"}}
</LinkTo>
<td class="d-admin-row__controls controls">
<div class="d-admin-row__controls-options">
<LinkTo
@route="adminWebHooks.edit"
@model={{webhook}}
class="btn btn-default no-text"
title={{i18n "admin.web_hooks.edit"}}
>
{{d-icon "far-pen-to-square"}}
</LinkTo>
<DButton
@action={{fn this.destroyWebhook webhook}}
@icon="xmark"
@title="delete"
class="destroy btn-danger"
/>
<DButton
@action={{fn this.destroyWebhook webhook}}
@icon="xmark"
@title="delete"
class="destroy btn-danger"
/>
</div>
</td>
</tr>
{{/each}}

View File

@ -33,7 +33,7 @@ module("Integration | Component | webhook-status", function (hooks) {
assert.dom().hasText("Failed");
});
test("iconName", async function (assert) {
test("statusLabelClass", async function (assert) {
const webhook = new CoreFabricators(getOwner(this)).webhook();
await render(<template>
<WebhookStatus
@ -42,30 +42,18 @@ module("Integration | Component | webhook-status", function (hooks) {
/>
</template>);
assert.dom(".d-icon-far-circle").exists();
assert.dom(".status-label").hasClass("--inactive");
webhook.set("last_delivery_status", 2);
await rerender();
assert.dom(".status-label").hasClass("--critical");
assert.dom(".d-icon-circle-xmark").exists();
});
test("iconClass", async function (assert) {
const webhook = new CoreFabricators(getOwner(this)).webhook();
await render(<template>
<WebhookStatus
@deliveryStatuses={{DELIVERY_STATUSES}}
@webhook={{webhook}}
/>
</template>);
assert.dom(".d-icon").hasClass("text-muted");
webhook.set("last_delivery_status", 2);
webhook.set("last_delivery_status", 3);
await rerender();
assert.dom(".status-label").hasClass("--success");
assert.dom(".d-icon").hasClass("text-danger");
webhook.set("last_delivery_status", 4);
await rerender();
assert.dom(".status-label").hasClass("--inactive");
});
});

View File

@ -3,7 +3,8 @@
$mobile-breakpoint: 700px;
:root {
--space-1: 0.25rem;
--space-0: 0.125rem; //2px
--space-1: 0.25rem; //4px
--space-2: calc(0.25rem * 2);
--space-3: calc(0.25rem * 3);
--space-4: calc(0.25rem * 4);

View File

@ -67,25 +67,27 @@
}
}
// Default
.status-label {
--d-border-radius: var(--space-4);
--status-icon-diameter: 8px;
display: flex;
flex-wrap: nowrap;
width: fit-content;
background-color: var(--primary-low);
padding: var(--space-1) var(--space-2);
padding: var(--space-0) var(--space-2);
border-radius: var(--d-border-radius);
.status-label-indicator {
display: inline-block;
width: 6px;
height: 6px;
width: var(--status-icon-diameter);
height: var(--status-icon-diameter);
border-radius: 50%;
background-color: var(--primary-high);
flex-shrink: 0;
margin-right: var(--space-1);
margin-top: 0.4rem;
margin-top: 0.35rem;
}
.status-label-text {
@ -93,6 +95,45 @@
font-size: var(--font-down-1);
}
}
// Success badge
.status-label.--success {
background-color: var(--success-low);
.status-label-indicator {
background-color: var(--success);
}
.status-label-text {
color: var(--success-hover);
}
}
// Critical badge
.status-label.--critical {
background-color: var(--danger-low);
.status-label-indicator {
background-color: var(--danger);
}
.status-label-text {
color: var(--danger-hover);
}
}
// Inactive badge
.status-label.--inactive {
background-color: var(--primary-low);
.status-label-indicator {
background-color: var(--primary-high);
}
.status-label-text {
color: var(--primary-high);
}
}
}
.d-admin-row__overview {

View File

@ -1,44 +1,24 @@
// Styles for admin/api
table.web-hooks.grid {
td.delivery-status {
div {
display: flex;
align-items: center;
}
.d-icon {
margin-right: 0.25em;
}
}
td.payload-url {
.d-admin-table.web-hooks {
.d-admin-row__overview.payload-url {
word-wrap: break-word;
max-width: 55vw;
}
td.controls {
display: flex;
button {
margin-left: 0.25em;
}
}
@media screen and (min-width: 550px) {
tbody {
tr {
grid-template-columns: 0.5fr repeat(2, 1fr) 0.5fr;
}
max-width: 20vw;
td.controls {
text-align: right;
}
@include breakpoint(medium) {
max-width: 70vw;
}
}
@include breakpoint(mobile-extra-large) {
tbody {
tr {
grid-template-columns: 0.5fr 1fr;
}
.d-admin-row__detail.description {
@include breakpoint(medium) {
display: block;
}
td.controls {
grid-row: 2;
.d-admin-row__mobile-label {
@include breakpoint(medium) {
display: block;
}
}
}
}