Add admin restart flow and loading indicators

This commit is contained in:
2026-07-08 13:34:43 +03:00
parent ae13070eba
commit 838dea0e03
11 changed files with 639 additions and 127 deletions

View File

@@ -10,6 +10,10 @@
--motion-panel: 220ms;
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-standard: cubic-bezier(0.22, 0.72, 0.18, 1);
--busy-ring-thickness: 2px;
--busy-ring-speed: 1.6s;
--busy-ring-long: 108px;
--busy-ring-short: 64px;
--surface-canvas: #101216;
--surface-panel: #131720;
--surface-raised: #151923;
@@ -91,6 +95,8 @@ button:disabled {
.ui-button,
.ui-icon-button {
appearance: none;
position: relative;
isolation: isolate;
display: inline-flex;
align-items: center;
justify-content: center;
@@ -102,6 +108,7 @@ button:disabled {
cursor: pointer;
font-weight: 750;
line-height: 1;
overflow: visible;
text-decoration: none;
transition:
background-color var(--motion-fast) var(--ease-out),
@@ -140,6 +147,18 @@ button:disabled {
transform: none;
}
.ui-button.is-loading:disabled,
.ui-icon-button.is-loading:disabled {
cursor: progress;
opacity: 1;
}
.ui-button > :not(.ui-busy-ring),
.ui-icon-button > :not(.ui-busy-ring) {
position: relative;
z-index: 1;
}
.ui-button--sm {
min-height: 32px;
padding: 6px 10px;
@@ -313,13 +332,58 @@ button:disabled {
min-width: 0;
}
.ui-button-spinner {
width: 14px;
height: 14px;
border: 2px solid currentColor;
border-top-color: transparent;
border-radius: 999px;
animation: spin 0.75s linear infinite;
.ui-busy-ring {
position: absolute;
display: block;
z-index: 2;
inset: 0;
overflow: hidden;
border-radius: inherit;
pointer-events: none;
}
.ui-busy-ring-segment {
position: absolute;
display: block;
background: #93c5fd;
box-shadow:
0 0 8px rgba(96, 165, 250, 0.95),
0 0 16px rgba(34, 197, 94, 0.36);
opacity: 0;
}
.ui-busy-ring-segment.top,
.ui-busy-ring-segment.bottom {
width: var(--busy-ring-long);
height: var(--busy-ring-thickness);
background: linear-gradient(90deg, transparent, #60a5fa 24%, #bbf7d0 54%, transparent);
}
.ui-busy-ring-segment.right,
.ui-busy-ring-segment.left {
width: var(--busy-ring-thickness);
height: var(--busy-ring-short);
background: linear-gradient(180deg, transparent, #60a5fa 24%, #bbf7d0 54%, transparent);
}
.ui-busy-ring-segment.top {
top: 0;
animation: finder-border-top var(--busy-ring-speed) linear infinite;
}
.ui-busy-ring-segment.right {
right: 0;
animation: finder-border-right var(--busy-ring-speed) linear infinite;
}
.ui-busy-ring-segment.bottom {
bottom: 0;
animation: finder-border-bottom var(--busy-ring-speed) linear infinite;
}
.ui-busy-ring-segment.left {
left: 0;
animation: finder-border-left var(--busy-ring-speed) linear infinite;
}
.ui-tabs {
@@ -387,6 +451,8 @@ button:disabled {
}
.ui-status-pill {
position: relative;
isolation: isolate;
display: inline-flex;
align-items: center;
width: fit-content;
@@ -399,6 +465,11 @@ button:disabled {
font-weight: 750;
}
.ui-status-pill > :not(.ui-busy-ring) {
position: relative;
z-index: 1;
}
.ui-status-pill--ok {
border-color: rgba(34, 197, 94, 0.38);
color: #86efac;
@@ -466,10 +537,8 @@ button:disabled {
}
.ui-action-menu-popover {
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 8;
position: fixed;
z-index: 80;
min-width: 172px;
border: 1px solid var(--border-strong);
border-radius: 4px;
@@ -480,6 +549,10 @@ button:disabled {
animation: ui-popover-in var(--motion-standard) var(--ease-out);
}
.ui-action-menu-popover[data-placement="top"] {
transform-origin: bottom right;
}
.ui-action-menu-popover button {
width: 100%;
min-height: 34px;
@@ -563,36 +636,36 @@ button:disabled {
.ui-service-border-glow-segment.top,
.ui-service-border-glow-segment.bottom {
width: 108px;
height: 2px;
width: var(--busy-ring-long);
height: var(--busy-ring-thickness);
background: linear-gradient(90deg, transparent, #60a5fa 24%, #bbf7d0 54%, transparent);
}
.ui-service-border-glow-segment.right,
.ui-service-border-glow-segment.left {
width: 2px;
height: 64px;
width: var(--busy-ring-thickness);
height: var(--busy-ring-short);
background: linear-gradient(180deg, transparent, #60a5fa 24%, #bbf7d0 54%, transparent);
}
.ui-service-border-glow-segment.top {
top: 0;
animation: finder-border-top 1.6s linear infinite;
animation: finder-border-top var(--busy-ring-speed) linear infinite;
}
.ui-service-border-glow-segment.right {
right: 0;
animation: finder-border-right 1.6s linear infinite;
animation: finder-border-right var(--busy-ring-speed) linear infinite;
}
.ui-service-border-glow-segment.bottom {
bottom: 0;
animation: finder-border-bottom 1.6s linear infinite;
animation: finder-border-bottom var(--busy-ring-speed) linear infinite;
}
.ui-service-border-glow-segment.left {
left: 0;
animation: finder-border-left 1.6s linear infinite;
animation: finder-border-left var(--busy-ring-speed) linear infinite;
}
.ui-service-dot {
@@ -621,11 +694,8 @@ button:disabled {
}
.ui-service-row--checking .ui-service-dot {
border: 2px solid var(--focus-ring);
border-top-color: transparent;
background: transparent;
box-shadow: none;
animation: spin 0.75s linear infinite;
background: #60a5fa;
box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.14);
}
.ui-service-text {
@@ -835,6 +905,54 @@ button:disabled {
content: "";
}
.admin-prompt {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 10px;
align-items: center;
min-width: 0;
border: 1px solid rgba(245, 158, 11, 0.44);
border-radius: 4px;
background: rgba(120, 53, 15, 0.18);
margin-bottom: 10px;
padding: 8px 10px;
}
.admin-prompt-icon {
display: grid;
place-items: center;
width: 32px;
min-width: 32px;
height: 32px;
border: 1px solid rgba(245, 158, 11, 0.28);
border-radius: 4px;
background: rgba(15, 23, 42, 0.42);
color: #fcd34d;
}
.admin-prompt-copy {
display: grid;
gap: 2px;
min-width: 0;
}
.admin-prompt-copy strong {
color: #f8fafc;
font-size: 13px;
font-weight: 800;
}
.admin-prompt-copy span {
color: #c7b489;
font-size: 12px;
overflow-wrap: anywhere;
}
.admin-prompt-action {
min-width: 230px;
white-space: nowrap;
}
.tab-panel-frame {
min-width: 0;
will-change: opacity, transform;
@@ -887,6 +1005,8 @@ button:disabled {
}
.proxy-overview {
position: relative;
isolation: isolate;
display: block;
border: 1px solid #2b3342;
border-radius: 4px;
@@ -894,6 +1014,11 @@ button:disabled {
padding: 8px 10px;
}
.proxy-overview > :not(.ui-busy-ring) {
position: relative;
z-index: 1;
}
.proxy-overview.ok {
border-color: rgba(34, 197, 94, 0.36);
}
@@ -915,6 +1040,8 @@ button:disabled {
}
.summary-status-control {
position: relative;
isolation: isolate;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 12px;
@@ -925,6 +1052,11 @@ button:disabled {
padding: 13px 14px;
}
.summary-status-control > :not(.ui-busy-ring) {
position: relative;
z-index: 1;
}
.summary-status-control.ok {
border-color: rgba(34, 197, 94, 0.44);
}
@@ -965,10 +1097,8 @@ button:disabled {
}
.summary-status-dot.checking {
border: 2px solid #3b82f6;
border-top-color: transparent;
background: transparent;
animation: spin 0.75s linear infinite;
background: #60a5fa;
box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.14);
}
.summary-status-copy span,
@@ -1070,6 +1200,8 @@ button:disabled {
}
.summary-card {
position: relative;
isolation: isolate;
display: grid;
gap: 5px;
align-content: start;
@@ -1082,6 +1214,11 @@ button:disabled {
text-align: left;
}
.summary-card > :not(.ui-busy-ring) {
position: relative;
z-index: 1;
}
button.summary-card {
cursor: pointer;
}
@@ -1227,36 +1364,36 @@ button.summary-card:hover {
.finder-border-glow-segment.top,
.finder-border-glow-segment.bottom {
width: 108px;
height: 2px;
width: var(--busy-ring-long);
height: var(--busy-ring-thickness);
background: linear-gradient(90deg, transparent, #60a5fa 24%, #bbf7d0 54%, transparent);
}
.finder-border-glow-segment.right,
.finder-border-glow-segment.left {
width: 2px;
height: 64px;
width: var(--busy-ring-thickness);
height: var(--busy-ring-short);
background: linear-gradient(180deg, transparent, #60a5fa 24%, #bbf7d0 54%, transparent);
}
.finder-border-glow-segment.top {
top: 0;
animation: finder-border-top 1.6s linear infinite;
animation: finder-border-top var(--busy-ring-speed) linear infinite;
}
.finder-border-glow-segment.right {
right: 0;
animation: finder-border-right 1.6s linear infinite;
animation: finder-border-right var(--busy-ring-speed) linear infinite;
}
.finder-border-glow-segment.bottom {
bottom: 0;
animation: finder-border-bottom 1.6s linear infinite;
animation: finder-border-bottom var(--busy-ring-speed) linear infinite;
}
.finder-border-glow-segment.left {
left: 0;
animation: finder-border-left 1.6s linear infinite;
animation: finder-border-left var(--busy-ring-speed) linear infinite;
}
.finder-text,
@@ -1305,11 +1442,8 @@ button.summary-card:hover {
}
.finder-card.checking .status-light {
border: 2px solid #3b82f6;
border-top-color: transparent;
background: transparent;
box-shadow: none;
animation: spin 0.75s linear infinite;
background: #60a5fa;
box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.14);
}
.setup-strip {
@@ -1838,6 +1972,8 @@ button.summary-card:hover {
}
.route-chain-segment {
position: relative;
isolation: isolate;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
grid-template-areas:
@@ -1856,6 +1992,11 @@ button.summary-card:hover {
width: 100%;
}
.route-chain-segment > :not(.ui-busy-ring) {
position: relative;
z-index: 1;
}
.route-chain-segment:hover {
border-color: #3b82f6;
background: #141d2b;
@@ -1909,11 +2050,8 @@ button.summary-card:hover {
}
.route-chain-segment.checking .route-chain-dot {
border: 2px solid #3b82f6;
border-top-color: transparent;
background: transparent;
box-shadow: none;
animation: spin 0.75s linear infinite;
background: #60a5fa;
box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.14);
}
.apps-section {
@@ -2196,9 +2334,17 @@ button.summary-card:hover {
}
.tile-loading {
position: relative;
isolation: isolate;
display: inline-flex;
align-items: center;
min-height: 30px;
border: 1px solid rgba(59, 130, 246, 0.42);
border-radius: 4px;
background: #111720;
color: #dbeafe;
font-weight: 800;
animation: pulse 1s ease-in-out infinite;
padding: 6px 9px;
}
.app-add-skeleton {
@@ -2327,14 +2473,12 @@ button.summary-card:hover {
}
.list-skeleton span {
position: relative;
isolation: isolate;
min-height: 56px;
border: 1px solid #2b3342;
border-radius: 4px;
background:
linear-gradient(90deg, transparent, rgba(148, 163, 184, 0.12), transparent),
#131720;
background-size: 220% 100%;
animation: shimmer 1.15s linear infinite;
background: #131720;
}
.subscription-line .ui-button {
@@ -2514,18 +2658,6 @@ button.summary-card:hover {
color: #b6c2d4;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
50% {
opacity: 0.45;
}
}
@keyframes finder-border-top {
0% {
left: -116px;
@@ -2600,16 +2732,6 @@ button.summary-card:hover {
}
}
@keyframes shimmer {
from {
background-position: 220% 0;
}
to {
background-position: -220% 0;
}
}
@keyframes panel-swipe-right {
from {
opacity: 0;
@@ -2637,12 +2759,14 @@ button.summary-card:hover {
@media (prefers-reduced-motion: reduce) {
.tab-panel-frame,
.tab-panel,
.ui-busy-ring,
.ui-busy-ring-segment,
.ui-service-border-glow,
.finder-card .finder-border-glow,
.finder-border-glow-segment,
.ui-service-border-glow-segment,
.status-light,
.summary-status-dot.checking,
.list-skeleton span,
.tile-loading {
.summary-status-dot.checking {
animation: none;
transition: none;
}
@@ -2662,6 +2786,18 @@ button.summary-card:hover {
padding: 12px 12px 16px;
}
.admin-prompt {
grid-template-columns: auto minmax(0, 1fr);
align-items: start;
gap: 8px;
}
.admin-prompt-action {
grid-column: 1 / -1;
width: 100%;
min-width: 0;
}
.app-row {
align-items: stretch;
flex-direction: column;