This is an automated email from the ASF dual-hosted git repository.

jscheffl pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 48c824aaf51 Feature/add state badge to edge react UI (#54993)
48c824aaf51 is described below

commit 48c824aaf51f77d5923fd341529932c86ecde650
Author: Jens Scheffler <[email protected]>
AuthorDate: Thu Sep 4 18:00:17 2025 +0200

    Feature/add state badge to edge react UI (#54993)
    
    * Add colored status badge to Edge Worker react UI
    
    * Remove outdated comment after eslint exception was removed
---
 .../providers/edge3/plugins/www/dist/main.umd.cjs  | 34 +++++------
 .../plugins/www/src/components/StateBadge.tsx      | 43 +++++++++++++
 .../edge3/plugins/www/src/components/StateIcon.tsx | 58 ++++++++++++++++++
 .../www/src/components/WorkerStateBadge.tsx        | 71 ++++++++++++++++++++++
 .../plugins/www/src/components/WorkerStateIcon.tsx | 57 +++++++++++++++++
 .../edge3/plugins/www/src/pages/JobsPage.tsx       |  6 +-
 .../edge3/plugins/www/src/pages/WorkerPage.tsx     | 18 +++++-
 providers/edge3/www-hash.txt                       |  2 +-
 8 files changed, 266 insertions(+), 23 deletions(-)

diff --git 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs
index 03b4ad2a398..8a897c96b35 100644
--- a/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs
+++ b/providers/edge3/src/airflow/providers/edge3/plugins/www/dist/main.umd.cjs
@@ -1,4 +1,4 @@
-(function(A,Y){typeof exports=="object"&&typeof 
module<"u"?module.exports=Y(require("react"),require("react-dom")):typeof 
define=="function"&&define.amd?define(["react","react-dom"],Y):(A=typeof 
globalThis<"u"?globalThis:A||self,A.AirflowPlugin=Y(A.React))})(this,function(A){"use
 strict";var MT=Object.defineProperty;var Pm=A=>{throw TypeError(A)};var 
$T=(A,Y,C)=>Y in 
A?MT(A,Y,{enumerable:!0,configurable:!0,writable:!0,value:C}):A[Y]=C;var 
Ge=(A,Y,C)=>$T(A,typeof Y!="symbol"?Y+"":Y,C),vl= [...]
+(function(A,Y){typeof exports=="object"&&typeof 
module<"u"?module.exports=Y(require("react"),require("react-dom")):typeof 
define=="function"&&define.amd?define(["react","react-dom"],Y):(A=typeof 
globalThis<"u"?globalThis:A||self,A.AirflowPlugin=Y(A.React))})(this,function(A){"use
 strict";var dT=Object.defineProperty;var Vm=A=>{throw TypeError(A)};var 
hT=(A,Y,w)=>Y in 
A?dT(A,Y,{enumerable:!0,configurable:!0,writable:!0,value:w}):A[Y]=w;var 
qe=(A,Y,w)=>hT(A,typeof Y!="symbol"?Y+"":Y,w),bl= [...]
  * @license React
  * react-jsx-runtime.production.min.js
  *
@@ -6,27 +6,27 @@
  *
  * This source code is licensed under the MIT license found in the
  * LICENSE file in the root directory of this source tree.
- */var 
Vm=A,Fm=Symbol.for("react.element"),zm=Symbol.for("react.fragment"),Lm=Object.prototype.hasOwnProperty,Dm=Vm.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,Mm={key:!0,ref:!0,__self:!0,__source:!0};function
 kl(e,t,n){var r,i={},o=null,s=null;n!==void 0&&(o=""+n),t.key!==void 
0&&(o=""+t.key),t.ref!==void 0&&(s=t.ref);for(r in 
t)Lm.call(t,r)&&!Mm.hasOwnProperty(r)&&(i[r]=t[r]);if(e&&e.defaultProps)for(r 
in t=e.defaultProps,t)i[r]===void 0&&(i[r]=t[r]);return{$$t [...]
+ */var 
Mm=A,Bm=Symbol.for("react.element"),$m=Symbol.for("react.fragment"),jm=Object.prototype.hasOwnProperty,Wm=Mm.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,Hm={key:!0,ref:!0,__self:!0,__source:!0};function
 Ol(e,t,n){var r,i={},o=null,s=null;n!==void 0&&(o=""+n),t.key!==void 
0&&(o=""+t.key),t.ref!==void 0&&(s=t.ref);for(r in 
t)jm.call(t,r)&&!Hm.hasOwnProperty(r)&&(i[r]=t[r]);if(e&&e.defaultProps)for(r 
in t=e.defaultProps,t)i[r]===void 0&&(i[r]=t[r]);return{$$t [...]
  * react-is.production.min.js
  *
  * Copyright (c) Facebook, Inc. and its affiliates.
  *
  * This source code is licensed under the MIT license found in the
  * LICENSE file in the root directory of this source tree.
- */var be=typeof 
Symbol=="function"&&Symbol.for,us=be?Symbol.for("react.element"):60103,ds=be?Symbol.for("react.portal"):60106,Ti=be?Symbol.for("react.fragment"):60107,Ni=be?Symbol.for("react.strict_mode"):60108,_i=be?Symbol.for("react.profiler"):60114,Ai=be?Symbol.for("react.provider"):60109,Vi=be?Symbol.for("react.context"):60110,hs=be?Symbol.for("react.async_mode"):60111,Fi=be?Symbol.for("react.concurrent_mode"):60111,zi=be?Symbol.for("react.forward_ref"):60112,Li=be?Symbol.for("react
 [...]
+ */var ye=typeof 
Symbol=="function"&&Symbol.for,hs=ye?Symbol.for("react.element"):60103,fs=ye?Symbol.for("react.portal"):60106,Ni=ye?Symbol.for("react.fragment"):60107,_i=ye?Symbol.for("react.strict_mode"):60108,Ai=ye?Symbol.for("react.profiler"):60114,Vi=ye?Symbol.for("react.provider"):60109,Li=ye?Symbol.for("react.context"):60110,gs=ye?Symbol.for("react.async_mode"):60111,Fi=ye?Symbol.for("react.concurrent_mode"):60111,zi=ye?Symbol.for("react.forward_ref"):60112,Di=ye?Symbol.for("react
 [...]
       <svg width="46" height="15" style="left: -15.5px; position: absolute; 
top: 0; filter: drop-shadow(rgba(0, 0, 0, 0.4) 0px 1px 1.1px);">
         <g transform="translate(2 3)">
           <path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L 
31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z" style="stroke-width: 2px; stroke: 
white;"></path>
           <path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L 
31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z"></path>
         </g>
-      </svg>`,n.body.appendChild(r)};function 
YC(e){if(!(!e||e.ownerDocument.activeElement!==e))try{const{selectionStart:t,selectionEnd:n,value:r}=e,i=r.substring(0,t),o=r.substring(n);return{start:t,end:n,value:r,beforeTxt:i,afterTxt:o}}catch{}}function
 
QC(e,t){if(!(!e||e.ownerDocument.activeElement!==e)){if(!t){e.setSelectionRange(e.value.length,e.value.length);return}try{const{value:n}=e,{beforeTxt:r="",afterTxt:i="",start:o}=t;let
 s=n.length;if(n.endsWith(i))s=n.length-i.length;else  [...]
+      </svg>`,n.body.appendChild(r)};function 
tC(e){if(!(!e||e.ownerDocument.activeElement!==e))try{const{selectionStart:t,selectionEnd:n,value:r}=e,i=r.substring(0,t),o=r.substring(n);return{start:t,end:n,value:r,beforeTxt:i,afterTxt:o}}catch{}}function
 
nC(e,t){if(!(!e||e.ownerDocument.activeElement!==e)){if(!t){e.setSelectionRange(e.value.length,e.value.length);return}try{const{value:n}=e,{beforeTxt:r="",afterTxt:i="",start:o}=t;let
 s=n.length;if(n.endsWith(i))s=n.length-i.length;else  [...]
 )+\\(\\s*max(-device)?-${e}`,"i"),min:new 
RegExp(`\\(\\s*min(-device)?-${e}`,"i"),maxMin:new 
RegExp(`(!?\\(\\s*max(-device)?-${e})(.|
-)+\\(\\s*min(-device)?-${e}`,"i"),max:new 
RegExp(`\\(\\s*max(-device)?-${e}`,"i")}),Xw=qd("width"),Yw=qd("height"),Kd=e=>({isMin:eh(e.minMax,e.maxMin,e.min),isMax:eh(e.maxMin,e.minMax,e.max)}),{isMin:Ta,isMax:Xd}=Kd(Xw),{isMin:Na,isMax:Yd}=Kd(Yw),Qd=/print/i,Jd=/^print$/i,Qw=/(-?\d*\.?\d+)(ch|em|ex|px|rem)/,Jw=/(\d)/,ei=Number.MAX_VALUE,Zw={ch:8.8984375,em:16,rem:16,ex:8.296875,px:1};function
 Zd(e){const t=Qw.exec(e)||(Ta(e)||Na(e)?Jw.exec(e):null);if(!t)return 
ei;if(t[0]==="0")return 0; [...]
-`).forEach(function(s){i=s.indexOf(":"),n=s.substring(0,i).trim().toLowerCase(),r=s.substring(i+1).trim(),!(!n||t[n]&&hR[n])&&(n==="set-cookie"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+",
 "+r:r)}),t},Tf=Symbol("internals");function ci(e){return 
e&&String(e).trim().toLowerCase()}function Do(e){return 
e===!1||e==null?e:w.isArray(e)?e.map(Do):String(e)}function gR(e){const 
t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let 
r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const pR=e=>/ [...]
-`)}getSetCookie(){return 
this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static
 from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const 
r=new this(t);return n.forEach(i=>r.set(i)),r}static accessor(t){const 
r=(this[Tf]=this[Tf]={accessors:{}}).accessors,i=this.prototype;function 
o(s){const a=ci(s);r[a]||(vR(i,s),r[a]=!0)}return 
w.isArray(t)?t.forEach(o):o(t),this}};He.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-A
 [...]
-`+o.map(Wf).join(`
-`):" "+Wf(o[0]):"as no adapter specified";throw new j("There is no suitable 
adapter to dispatch the request "+s,"ERR_NOT_SUPPORT")}return 
r},adapters:il};function 
ol(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw
 new ur(null,e)}function Hf(e){return 
ol(e),e.headers=He.from(e.headers),e.data=nl.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),jf.getAdapter(e.ad
 [...]
-`+o):r.stack=o}catch{}}throw r}}_request(t,n){typeof 
t=="string"?(n=n||{},n.url=t):n=t||{},n=Rn(this.defaults,n);const{transitional:r,paramsSerializer:i,headers:o}=n;r!==void
 
0&&jo.assertOptions(r,{silentJSONParsing:Ot.transitional(Ot.boolean),forcedJSONParsing:Ot.transitional(Ot.boolean),clarifyTimeoutError:Ot.transitional(Ot.boolean)},!1),i!=null&&(w.isFunction(i)?n.paramsSerializer={serialize:i}:jo.assertOptions(i,{encode:Ot.function,serialize:Ot.function},!0)),n.allowAbsoluteUrls!==v
 [...]
+)+\\(\\s*min(-device)?-${e}`,"i"),max:new 
RegExp(`\\(\\s*max(-device)?-${e}`,"i")}),ew=Kd("width"),tw=Kd("height"),Xd=e=>({isMin:th(e.minMax,e.maxMin,e.min),isMax:th(e.maxMin,e.minMax,e.max)}),{isMin:Na,isMax:Yd}=Xd(ew),{isMin:_a,isMax:Qd}=Xd(tw),Jd=/print/i,Zd=/^print$/i,nw=/(-?\d*\.?\d+)(ch|em|ex|px|rem)/,rw=/(\d)/,ti=Number.MAX_VALUE,iw={ch:8.8984375,em:16,rem:16,ex:8.296875,px:1};function
 eh(e){const t=nw.exec(e)||(Na(e)||_a(e)?rw.exec(e):null);if(!t)return 
ti;if(t[0]==="0")return 0; [...]
+`).forEach(function(s){i=s.indexOf(":"),n=s.substring(0,i).trim().toLowerCase(),r=s.substring(i+1).trim(),!(!n||t[n]&&bR[n])&&(n==="set-cookie"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+",
 "+r:r)}),t},_f=Symbol("internals");function ui(e){return 
e&&String(e).trim().toLowerCase()}function Bo(e){return 
e===!1||e==null?e:k.isArray(e)?e.map(Bo):String(e)}function xR(e){const 
t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let 
r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const SR=e=>/ [...]
+`)}getSetCookie(){return 
this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static
 from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const 
r=new this(t);return n.forEach(i=>r.set(i)),r}static accessor(t){const 
r=(this[_f]=this[_f]={accessors:{}}).accessors,i=this.prototype;function 
o(s){const a=ui(s);r[a]||(wR(i,s),r[a]=!0)}return 
k.isArray(t)?t.forEach(o):o(t),this}};Ue.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-A
 [...]
+`+o.map(Hf).join(`
+`):" "+Hf(o[0]):"as no adapter specified";throw new W("There is no suitable 
adapter to dispatch the request "+s,"ERR_NOT_SUPPORT")}return 
r},adapters:ol};function 
sl(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw
 new dr(null,e)}function Gf(e){return 
sl(e),e.headers=Ue.from(e.headers),e.data=rl.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),Uf.getAdapter(e.ad
 [...]
+`+o):r.stack=o}catch{}}throw r}}_request(t,n){typeof 
t=="string"?(n=n||{},n.url=t):n=t||{},n=Pn(this.defaults,n);const{transitional:r,paramsSerializer:i,headers:o}=n;r!==void
 
0&&Uo.assertOptions(r,{silentJSONParsing:It.transitional(It.boolean),forcedJSONParsing:It.transitional(It.boolean),clarifyTimeoutError:It.transitional(It.boolean)},!1),i!=null&&(k.isFunction(i)?n.paramsSerializer={serialize:i}:Uo.assertOptions(i,{encode:It.function,serialize:It.function},!0)),n.allowAbsoluteUrls!==v
 [...]
  * @remix-run/router v1.23.0
  *
  * Copyright (c) Remix Software Inc.
@@ -35,7 +35,7 @@
  * LICENSE.md file in the root directory of this source tree.
  *
  * @license MIT
- */function ui(){return 
ui=Object.assign?Object.assign.bind():function(e){for(var 
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in 
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return 
e},ui.apply(this,arguments)}var 
Qt;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(Qt||(Qt={}));const
 Qf="popstate";function GR(e){e===void 0&&(e={});function 
t(r,i){let{pathname:o,search:s,hash:a}=r.location;return 
ll("",{pathname:o,search:s,hash:a},i.state&&i.state.usr|| [...]
+ */function di(){return 
di=Object.assign?Object.assign.bind():function(e){for(var 
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in 
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return 
e},di.apply(this,arguments)}var 
Jt;(function(e){e.Pop="POP",e.Push="PUSH",e.Replace="REPLACE"})(Jt||(Jt={}));const
 Zf="popstate";function JR(e){e===void 0&&(e={});function 
t(r,i){let{pathname:o,search:s,hash:a}=r.location;return 
cl("",{pathname:o,search:s,hash:a},i.state&&i.state.usr|| [...]
  * React Router v6.30.1
  *
  * Copyright (c) Remix Software Inc.
@@ -44,7 +44,7 @@
  * LICENSE.md file in the root directory of this source tree.
  *
  * @license MIT
- */function di(){return 
di=Object.assign?Object.assign.bind():function(e){for(var 
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in 
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return 
e},di.apply(this,arguments)}const 
Go=C.createContext(null),ag=C.createContext(null),Zt=C.createContext(null),qo=C.createContext(null),Tn=C.createContext({outlet:null,matches:[],isDataRoute:!1}),lg=C.createContext(null);function
 vP(e,t){let{relative:n}=t===void 0?{}:t;hi()||ue(!1);let{b [...]
+ */function hi(){return 
hi=Object.assign?Object.assign.bind():function(e){for(var 
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in 
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return 
e},hi.apply(this,arguments)}const 
Ko=w.createContext(null),cg=w.createContext(null),en=w.createContext(null),Xo=w.createContext(null),Nn=w.createContext({outlet:null,matches:[],isDataRoute:!1}),ug=w.createContext(null);function
 wP(e,t){let{relative:n}=t===void 0?{}:t;fi()||ue(!1);let{b [...]
  * React Router DOM v6.30.1
  *
  * Copyright (c) Remix Software Inc.
@@ -53,7 +53,7 @@
  * LICENSE.md file in the root directory of this source tree.
  *
  * @license MIT
- */function Xo(){return 
Xo=Object.assign?Object.assign.bind():function(e){for(var 
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in 
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return 
e},Xo.apply(this,arguments)}function gg(e,t){if(e==null)return{};var 
n={},r=Object.keys(e),i,o;for(o=0;o<r.length;o++)i=r[o],!(t.indexOf(i)>=0)&&(n[i]=e[i]);return
 n}function zP(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function 
LP(e,t){return e.button===0&&(!t||t==="_sel [...]
+ */function Qo(){return 
Qo=Object.assign?Object.assign.bind():function(e){for(var 
t=1;t<arguments.length;t++){var n=arguments[t];for(var r in 
n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return 
e},Qo.apply(this,arguments)}function mg(e,t){if(e==null)return{};var 
n={},r=Object.keys(e),i,o;for(o=0;o<r.length;o++)i=r[o],!(t.indexOf(i)>=0)&&(n[i]=e[i]);return
 n}function jP(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function 
WP(e,t){return e.button===0&&(!t||t==="_sel [...]
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -70,7 +70,7 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- */const Pg=5e3;/*!
+ */const Vg=5e3;/*!
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -87,7 +87,7 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- */const OT=e=>{const[t,n]=A.useState(0);return 
A.useEffect(()=>{if(!e.current)return;const r=new ResizeObserver(i=>{for(const 
o of i)n(o.contentRect.width)});return 
r.observe(e.current),()=>{r.disconnect()}},[e]),t};/*!
+ */const H2=e=>{const[t,n]=A.useState(0);return 
A.useEffect(()=>{if(!e.current)return;const r=new ResizeObserver(i=>{for(const 
o of i)n(o.contentRect.width)});return 
r.observe(e.current),()=>{r.disconnect()}},[e]),t};/*!
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -104,7 +104,7 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- */const Tg="token",IT=()=>{const e=document.cookie.split(";");for(const t of 
e){const[n,r]=t.split("=");if((n==null?void 0:n.trim())==="_token"&&r!==void 
0)return localStorage.setItem(Tg,r),document.cookie="_token=; expires=Sat, 01 
Jan 2000 00:00:00 UTC; path=/;",r}},RT=e=>{const 
t=localStorage.getItem(Tg)??IT();return t!==void 
0&&(e.headers.Authorization=`Bearer 
${t}`),e},PT=()=>{const{data:e,error:t}=gT(void 
0,{enabled:!0,refetchInterval:Pg});return e?k.jsx($t,{p:2,children:k.jsxs(Wh, 
[...]
+ */const Lg="token",U2=()=>{const e=document.cookie.split(";");for(const t of 
e){const[n,r]=t.split("=");if((n==null?void 0:n.trim())==="_token"&&r!==void 
0)return localStorage.setItem(Lg,r),document.cookie="_token=; expires=Sat, 01 
Jan 2000 00:00:00 UTC; path=/;",r}},G2=e=>{const 
t=localStorage.getItem(Lg)??U2();return t!==void 
0&&(e.headers.Authorization=`Bearer 
${t}`),e},q2=()=>{const{data:e,error:t}=x2(void 
0,{enabled:!0,refetchInterval:Vg});return e?C.jsx($t,{p:2,children:C.jsxs(Hh, 
[...]
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -121,4 +121,4 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- */const 
en=(e,t="white")=>({solid:{value:`{colors.${e}.600}`},contrast:{value:{_light:"white",_dark:t}},fg:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}},muted:{value:{_light:`{colors.${e}.200}`,_dark:`{colors.${e}.800}`}},subtle:{value:{_light:`{colors.${e}.100}`,_dark:`{colors.${e}.900}`}},emphasized:{value:{_light:`{colors.${e}.300}`,_dark:`{colors.${e}.700}`}},focusRing:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}}}),AT=Oa({theme:{tokens:{colors:{suc
 [...]
+ */const 
tn=(e,t="white")=>({solid:{value:`{colors.${e}.600}`},contrast:{value:{_light:"white",_dark:t}},fg:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}},muted:{value:{_light:`{colors.${e}.200}`,_dark:`{colors.${e}.800}`}},subtle:{value:{_light:`{colors.${e}.100}`,_dark:`{colors.${e}.900}`}},emphasized:{value:{_light:`{colors.${e}.300}`,_dark:`{colors.${e}.700}`}},focusRing:{value:{_light:`{colors.${e}.800}`,_dark:`{colors.${e}.200}`}}}),oT=Ia({theme:{tokens:{colors:{suc
 [...]
diff --git 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateBadge.tsx
 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateBadge.tsx
new file mode 100644
index 00000000000..cbda0810e61
--- /dev/null
+++ 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateBadge.tsx
@@ -0,0 +1,43 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Badge, type BadgeProps } from "@chakra-ui/react";
+import type { TaskInstanceState } from "openapi/requests/types.gen";
+import * as React from "react";
+
+import { StateIcon } from "./StateIcon";
+
+export type Props = {
+  readonly state?: TaskInstanceState | null;
+} & BadgeProps;
+
+export const StateBadge = React.forwardRef<HTMLDivElement, Props>(({ children, 
state, ...rest }, ref) => (
+  <Badge
+    borderRadius="full"
+    colorPalette={state === null ? "none" : state}
+    fontSize="sm"
+    px={children === undefined ? 1 : 2}
+    py={1}
+    ref={ref}
+    variant="solid"
+    {...rest}
+  >
+    {state === undefined ? undefined : <StateIcon state={state} />}
+    {children}
+  </Badge>
+));
diff --git 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateIcon.tsx
 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateIcon.tsx
new file mode 100644
index 00000000000..6189b599012
--- /dev/null
+++ 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/StateIcon.tsx
@@ -0,0 +1,58 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import type { TaskInstanceState } from "openapi/requests/types.gen";
+import type { IconBaseProps } from "react-icons";
+import { FiActivity, FiCalendar, FiRepeat, FiSkipForward, FiSlash, FiWatch, 
FiX } from "react-icons/fi";
+import { LuCalendarSync, LuCheck, LuCircleDashed, LuCircleFadingArrowUp, 
LuRedo2 } from "react-icons/lu";
+import { PiQueue } from "react-icons/pi";
+
+type Props = {
+  readonly state?: TaskInstanceState | null;
+} & IconBaseProps;
+
+export const StateIcon = ({ state, ...rest }: Props) => {
+  switch (state) {
+    case "deferred":
+      return <FiWatch {...rest} />;
+    case "failed":
+      return <FiX {...rest} />;
+    case "queued":
+      return <PiQueue {...rest} />;
+    case "removed":
+      return <FiSlash {...rest} />;
+    case "restarting":
+      return <FiRepeat {...rest} />;
+    case "running":
+      return <FiActivity {...rest} />;
+    case "scheduled":
+      return <FiCalendar {...rest} />;
+    case "skipped":
+      return <FiSkipForward {...rest} />;
+    case "success":
+      return <LuCheck {...rest} />;
+    case "up_for_reschedule":
+      return <LuCalendarSync {...rest} />;
+    case "up_for_retry":
+      return <LuRedo2 {...rest} />;
+    case "upstream_failed":
+      return <LuCircleFadingArrowUp {...rest} />;
+    default:
+      return <LuCircleDashed {...rest} />;
+  }
+};
diff --git 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateBadge.tsx
 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateBadge.tsx
new file mode 100644
index 00000000000..d4269b2d2ab
--- /dev/null
+++ 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateBadge.tsx
@@ -0,0 +1,71 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Badge, type BadgeProps } from "@chakra-ui/react";
+import type { EdgeWorkerState } from "openapi/requests/types.gen";
+import * as React from "react";
+
+import { WorkerStateIcon } from "./WorkerStateIcon";
+
+const state2Color = (state: EdgeWorkerState | null | undefined) => {
+  switch (state) {
+    case "starting":
+    case "maintenance request":
+    case "maintenance exit":
+      return "yellow";
+    case "running":
+      return "green";
+    case "idle":
+      return "teal";
+    case "shutdown request":
+    case "terminating":
+      return "purple";
+    case "offline":
+    case "offline maintenance":
+      return "black";
+    case "unknown":
+      return "red";
+    case "maintenance mode":
+    case "maintenance pending":
+      return "orange";
+    default:
+      return "gray";
+  }
+};
+
+export type Props = {
+  readonly state?: EdgeWorkerState | null;
+} & BadgeProps;
+
+export const WorkerStateBadge = React.forwardRef<HTMLDivElement, Props>(
+  ({ children, state, ...rest }, ref) => (
+    <Badge
+      borderRadius="full"
+      colorPalette={state2Color(state)}
+      fontSize="sm"
+      px={children === undefined ? 1 : 2}
+      py={1}
+      ref={ref}
+      variant="solid"
+      {...rest}
+    >
+      {state === undefined ? undefined : <WorkerStateIcon state={state} />}
+      {children}
+    </Badge>
+  ),
+);
diff --git 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateIcon.tsx
 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateIcon.tsx
new file mode 100644
index 00000000000..753c1803528
--- /dev/null
+++ 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/components/WorkerStateIcon.tsx
@@ -0,0 +1,57 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import type { EdgeWorkerState } from "openapi/requests/types.gen";
+import type { IconBaseProps } from "react-icons";
+import { BiSolidLeaf } from "react-icons/bi";
+import { FiActivity, FiArrowDownRight, FiWatch } from "react-icons/fi";
+import { HiOutlineWrench, HiOutlineWrenchScrewdriver } from "react-icons/hi2";
+import { IoCloudOfflineOutline } from "react-icons/io5";
+import { LuCircleDashed } from "react-icons/lu";
+import { RiWifiOffLine } from "react-icons/ri";
+
+type Props = {
+  readonly state?: EdgeWorkerState | null;
+} & IconBaseProps;
+
+export const WorkerStateIcon = ({ state, ...rest }: Props) => {
+  switch (state) {
+    case "starting":
+      return <FiWatch {...rest} />;
+    case "running":
+      return <FiActivity {...rest} />;
+    case "idle":
+      return <BiSolidLeaf {...rest} />;
+    case "shutdown request":
+    case "terminating":
+      return <FiArrowDownRight {...rest} />;
+    case "offline":
+      return <IoCloudOfflineOutline {...rest} />;
+    case "unknown":
+      return <RiWifiOffLine {...rest} />;
+    case "maintenance request":
+    case "maintenance pending":
+    case "maintenance exit":
+      return <HiOutlineWrench {...rest} />;
+    case "maintenance mode":
+    case "offline maintenance":
+      return <HiOutlineWrenchScrewdriver {...rest} />;
+    default:
+      return <LuCircleDashed {...rest} />;
+  }
+};
diff --git 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/JobsPage.tsx
 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/JobsPage.tsx
index 15fd58d3823..5ace842f5dd 100644
--- 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/JobsPage.tsx
+++ 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/JobsPage.tsx
@@ -20,6 +20,7 @@ import { Box, Table } from "@chakra-ui/react";
 import { useUiServiceJobs } from "openapi/queries";
 
 import { ErrorAlert } from "src/components/ErrorAlert";
+import { StateBadge } from "src/components/StateBadge";
 import { autoRefreshInterval } from "src/utils";
 
 export const JobsPage = () => {
@@ -29,7 +30,6 @@ export const JobsPage = () => {
   });
 
   // TODO to make it proper
-  // Beautification of state like in Airflow 2
   // Use DataTable as component from Airflow-Core UI
   // Add sorting
   // Add filtering
@@ -63,7 +63,9 @@ export const JobsPage = () => {
                 <Table.Cell>{job.task_id}</Table.Cell>
                 <Table.Cell>{job.map_index}</Table.Cell>
                 <Table.Cell>{job.try_number}</Table.Cell>
-                <Table.Cell>{job.state}</Table.Cell>
+                <Table.Cell>
+                  <StateBadge state={job.state}>{job.state}</StateBadge>
+                </Table.Cell>
                 <Table.Cell>{job.queue}</Table.Cell>
                 <Table.Cell>{job.queued_dttm}</Table.Cell>
                 <Table.Cell>{job.edge_worker}</Table.Cell>
diff --git 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
index e8888961e0c..2af2adb6ecf 100644
--- 
a/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
+++ 
b/providers/edge3/src/airflow/providers/edge3/plugins/www/src/pages/WorkerPage.tsx
@@ -20,6 +20,7 @@ import { Box, Table } from "@chakra-ui/react";
 import { useUiServiceWorker } from "openapi/queries";
 
 import { ErrorAlert } from "src/components/ErrorAlert";
+import { WorkerStateBadge } from "src/components/WorkerStateBadge";
 import { autoRefreshInterval } from "src/utils";
 
 export const WorkerPage = () => {
@@ -29,7 +30,6 @@ export const WorkerPage = () => {
   });
 
   // TODO to make it proper
-  // Beautification of state like in Airflow 2
   // Use DataTable as component from Airflow-Core UI
   // Add actions for maintenance / delete of orphan worker
   // Add sorting
@@ -56,8 +56,20 @@ export const WorkerPage = () => {
             {data.workers.map((worker) => (
               <Table.Row key={worker.worker_name}>
                 <Table.Cell>{worker.worker_name}</Table.Cell>
-                <Table.Cell>{worker.state}</Table.Cell>
-                <Table.Cell>{worker.queues}</Table.Cell>
+                <Table.Cell>
+                  <WorkerStateBadge 
state={worker.state}>{worker.state}</WorkerStateBadge>
+                </Table.Cell>
+                <Table.Cell>
+                  {worker.queues ? (
+                    <ul>
+                      {worker.queues.map((queue) => (
+                        <li key={queue}>{queue}</li>
+                      ))}
+                    </ul>
+                  ) : (
+                    "(default)"
+                  )}
+                </Table.Cell>
                 <Table.Cell>{worker.first_online}</Table.Cell>
                 <Table.Cell>{worker.last_heartbeat}</Table.Cell>
                 <Table.Cell>{worker.jobs_active}</Table.Cell>
diff --git a/providers/edge3/www-hash.txt b/providers/edge3/www-hash.txt
index e3c21f4dab6..41772bc2d03 100644
--- a/providers/edge3/www-hash.txt
+++ b/providers/edge3/www-hash.txt
@@ -1 +1 @@
-33ac5e99982b8cf9795aff82a9b08baf5a5c2d13d71d1be72a5ffd664f8e14c1
+d780858c70355a081b2486871c18d5f3c579f04129b3b15e7c6f691a1a3a2c12

Reply via email to