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

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;
+}