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

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


The following commit(s) were added to refs/heads/main by this push:
     new d1313346d0 feat(gui): display the owner's avatar next to each CU 
(#3784)
d1313346d0 is described below

commit d1313346d0b54358185a1a0e54b89458221f277c
Author: yunyad <[email protected]>
AuthorDate: Fri Oct 3 12:43:52 2025 -0700

    feat(gui): display the owner's avatar next to each CU (#3784)
    
    ### PR Description
    
    This PR implements feature request #3578: displaying the owner's avatar
    next to each CU (Compute Unit).
    
    - Adds support for fetching and rendering the CU owner's avatar
    - Avatar is typically shown using a small inline image next to the CU
    label
    
    This change improves UI clarity and helps users quickly identify
    ownership in collaborative workflows.
    
    ### Demo
    ![Screen Recording 2025-09-30 at 5 38
    25 
PM](https://github.com/user-attachments/assets/0df49717-ae07-45cd-953c-907508e10864)
    
    Fix #3578
    
    ---------
    
    Co-authored-by: Xinyuan Lin <[email protected]>
---
 .../resource/ComputingUnitManagingResource.scala   | 42 +++++++++++++++++++---
 .../computing-unit-selection.component.html        | 16 +++++++++
 .../app/workspace/types/workflow-computing-unit.ts |  2 ++
 core/gui/src/styles.scss                           |  6 ++++
 4 files changed, 62 insertions(+), 4 deletions(-)

diff --git 
a/core/computing-unit-managing-service/src/main/scala/edu/uci/ics/texera/service/resource/ComputingUnitManagingResource.scala
 
b/core/computing-unit-managing-service/src/main/scala/edu/uci/ics/texera/service/resource/ComputingUnitManagingResource.scala
index 25e1d7524c..1065a74a76 100644
--- 
a/core/computing-unit-managing-service/src/main/scala/edu/uci/ics/texera/service/resource/ComputingUnitManagingResource.scala
+++ 
b/core/computing-unit-managing-service/src/main/scala/edu/uci/ics/texera/service/resource/ComputingUnitManagingResource.scala
@@ -27,6 +27,7 @@ import edu.uci.ics.texera.dao.SqlServer
 import edu.uci.ics.texera.dao.SqlServer.withTransaction
 import edu.uci.ics.texera.dao.jooq.generated.tables.daos.{
   ComputingUnitUserAccessDao,
+  UserDao,
   WorkflowComputingUnitDao
 }
 import edu.uci.ics.texera.dao.jooq.generated.tables.pojos.WorkflowComputingUnit
@@ -126,7 +127,9 @@ object ComputingUnitManagingResource {
       status: String,
       metrics: WorkflowComputingUnitMetrics,
       isOwner: Boolean,
-      accessPrivilege: EnumType
+      accessPrivilege: EnumType,
+      ownerGoogleAvatar: String,
+      ownerName: String
   )
 
   case class ComputingUnitLimitOptionsResponse(
@@ -397,6 +400,13 @@ class ComputingUnitManagingResource {
 
       wcDao.insert(computingUnit)
 
+      val userDao = new UserDao(ctx.configuration())
+      val ownerUser = Option(userDao.fetchOneByUid(user.getUid))
+      val ownerGoogleAvatar: String =
+        ownerUser.flatMap(u => 
Option(u.getGoogleAvatar).filter(_.nonEmpty)).orNull
+      val ownerUsername: String =
+        ownerUser.flatMap(u => Option(u.getName).filter(_.nonEmpty)).orNull
+
       // Retrieve generated cuid
       val cuid = ctx.lastID().intValue()
       val insertedUnit = wcDao.fetchOneByCuid(cuid)
@@ -442,7 +452,9 @@ class ComputingUnitManagingResource {
         getComputingUnitStatus(insertedUnit).toString,
         getComputingUnitMetrics(insertedUnit),
         isOwner = true,
-        accessPrivilege = PrivilegeEnum.WRITE
+        accessPrivilege = PrivilegeEnum.WRITE,
+        ownerGoogleAvatar,
+        ownerUsername
       )
     }
   }
@@ -490,6 +502,18 @@ class ComputingUnitManagingResource {
         }
 
       val allUnits = ownedUnits ++ sharedUnits
+      val ownerUids: List[Integer] = allUnits.map(_.getUid).distinct
+      val userDao = new UserDao(ctx.configuration())
+      val ownerInfoMap: Map[Integer, (String, String)] =
+        userDao
+          .fetchByUid(ownerUids: _*)
+          .asScala
+          .map { u =>
+            val avatar = Option(u.getGoogleAvatar).filter(_.nonEmpty).orNull
+            val name = Option(u.getName).filter(_.nonEmpty).orNull
+            u.getUid -> (avatar, name)
+          }
+          .toMap
 
       // If a Kubernetes pod has already disappeared (e.g., manually deleted 
or TTL
       // GC-ed by the cluster), we treat the corresponding computing unit as
@@ -529,7 +553,9 @@ class ComputingUnitManagingResource {
               isOwner = unit.getUid.equals(uid),
               accessPrivilege = privilege,
               status = getComputingUnitStatus(unit).toString,
-              metrics = getComputingUnitMetrics(unit)
+              metrics = getComputingUnitMetrics(unit),
+              ownerGoogleAvatar = ownerInfoMap.getOrElse(unit.getUid, (null, 
null))._1,
+              ownerName = ownerInfoMap.getOrElse(unit.getUid, (null, null))._2
             )
         }
     }
@@ -551,6 +577,12 @@ class ComputingUnitManagingResource {
   ): DashboardWorkflowComputingUnit = {
 
     val unit = getComputingUnitByCuid(context, cuid)
+    val userDao = new UserDao(context.configuration())
+    val ownerUser = Option(userDao.fetchOneByUid(unit.getUid))
+    val ownerGoogleAvatar: String =
+      ownerUser.flatMap(u => 
Option(u.getGoogleAvatar).filter(_.nonEmpty)).orNull
+    val ownerUsername: String =
+      ownerUser.flatMap(u => Option(u.getName).filter(_.nonEmpty)).orNull
 
     DashboardWorkflowComputingUnit(
       computingUnit = unit,
@@ -572,7 +604,9 @@ class ComputingUnitManagingResource {
           // Default privilege for non-owners without explicit access
           PrivilegeEnum.NONE
         }
-      }
+      },
+      ownerGoogleAvatar,
+      ownerUsername
     )
   }
 
diff --git 
a/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html
 
b/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html
index b4c2362f74..96debacec0 100644
--- 
a/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html
+++ 
b/core/gui/src/app/workspace/component/power-button/computing-unit-selection.component.html
@@ -67,6 +67,15 @@
     (nzVisibleChange)="onDropdownVisibilityChange($event)"
     class="computing-units-dropdown-button">
     <div class="button-content">
+      <texera-user-avatar
+        *ngIf="selectedComputingUnit"
+        [googleAvatar]="selectedComputingUnit ? 
selectedComputingUnit.ownerGoogleAvatar : ''"
+        userColor="grey"
+        [userName]="selectedComputingUnit ? selectedComputingUnit.ownerName : 
''"
+        [style.transform]="'scale(0.65)'"
+        [style.opacity]="0.7"
+        [style.padding-right.px]="2">
+      </texera-user-avatar>
       <nz-badge
         [nzStatus]="computeStatus()"
         [nz-tooltip]="selectedComputingUnit ? selectedComputingUnit.status : 
''"
@@ -106,6 +115,13 @@
         [nz-tooltip]="cannotSelectUnit(unit) ? getUnitStatusTooltip(unit) + '. 
Cannot select.' : ''"
         (click)="selectedComputingUnit = unit; 
selectComputingUnit(this.workflowId, unit?.computingUnit?.cuid)">
         <div class="computing-unit-row">
+          <texera-user-avatar
+            [googleAvatar]="unit.ownerGoogleAvatar"
+            userColor="grey"
+            [userName]="unit.ownerName || ''"
+            [style.transform]="'scale(0.65)'"
+            [style.opacity]="0.7">
+          </texera-user-avatar>
           <div class="computing-unit-name">
             <nz-badge
               [nzColor]="getBadgeColor(unit.status)"
diff --git a/core/gui/src/app/workspace/types/workflow-computing-unit.ts 
b/core/gui/src/app/workspace/types/workflow-computing-unit.ts
index f5b1f948aa..00aa9f15e1 100644
--- a/core/gui/src/app/workspace/types/workflow-computing-unit.ts
+++ b/core/gui/src/app/workspace/types/workflow-computing-unit.ts
@@ -50,4 +50,6 @@ export interface DashboardWorkflowComputingUnit {
   metrics: WorkflowComputingUnitMetrics;
   isOwner: boolean;
   accessPrivilege: "READ" | "WRITE" | "NONE";
+  ownerGoogleAvatar: string;
+  ownerName: string;
 }
diff --git a/core/gui/src/styles.scss b/core/gui/src/styles.scss
index be4dd8bc89..c0f39bf2f6 100644
--- a/core/gui/src/styles.scss
+++ b/core/gui/src/styles.scss
@@ -95,3 +95,9 @@ hr {
 .annotation-highlight {
   background-color: #6a5acd;
 }
+
+// makes avatar text centered in dropdown menu
+.ant-avatar-string {
+  position: relative;
+  left: 0;
+}

Reply via email to