The tasks page now shows the assignee

This commit is contained in:
Colin Dawson 2026-03-17 16:36:52 +00:00
parent a456fbbdbc
commit 4cf4228eed
2 changed files with 234 additions and 2 deletions

View File

@ -0,0 +1,226 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { GeneralIdRef } from "../../../../utils/GeneralIdRef";
import { Namespaces } from "../../../../i18n/i18n";
import roleService from "../../domains/serrvices/rolesService";
import userService from "../../users/services/usersService";
export interface AssigneePanelProps {
user?: GeneralIdRef;
role?: GeneralIdRef;
}
const roleNameCache = new Map<string, string | null>();
const roleLookupInFlight = new Map<string, Promise<string | null>>();
const userNameCache = new Map<string, string | null>();
const userLookupInFlight = new Map<string, Promise<string | null>>();
function getRoleCacheKey(role?: GeneralIdRef): string | null {
if (!role) {
return null;
}
if (role.guid) {
return `guid:${role.guid}`;
}
if (role.id !== undefined) {
return `id:${role.id.toString()}`;
}
return null;
}
function getUserCacheKey(user?: GeneralIdRef): string | null {
if (!user) {
return null;
}
if (user.guid) {
return `guid:${user.guid}`;
}
if (user.id !== undefined) {
return `id:${user.id.toString()}`;
}
return null;
}
async function getCachedRoleName(role: GeneralIdRef): Promise<string | null> {
const key = getRoleCacheKey(role);
if (!key) {
return null;
}
if (roleNameCache.has(key)) {
return roleNameCache.get(key) ?? null;
}
const inFlight = roleLookupInFlight.get(key);
if (inFlight) {
return inFlight;
}
const request = roleService
.getRole(role.id, role.guid)
.then((roleDetails) => {
const name = roleDetails?.name ?? null;
roleNameCache.set(key, name);
return name;
})
.catch(() => {
roleNameCache.set(key, null);
return null;
})
.finally(() => {
roleLookupInFlight.delete(key);
});
roleLookupInFlight.set(key, request);
return request;
}
async function getCachedUserName(user: GeneralIdRef): Promise<string | null> {
const key = getUserCacheKey(user);
if (!key) {
return null;
}
if (userNameCache.has(key)) {
return userNameCache.get(key) ?? null;
}
const inFlight = userLookupInFlight.get(key);
if (inFlight) {
return inFlight;
}
const request = userService
.getUser(user.id, user.guid)
.then((userDetails) => {
const name = userDetails?.displayName ?? null;
userNameCache.set(key, name);
return name;
})
.catch(() => {
userNameCache.set(key, null);
return null;
})
.finally(() => {
userLookupInFlight.delete(key);
});
userLookupInFlight.set(key, request);
return request;
}
const AssigneePanel: React.FC<AssigneePanelProps> = ({ user, role }) => {
const { t } = useTranslation(Namespaces.Common);
const [resolvedUserName, setResolvedUserName] = React.useState<string | null>(
null,
);
const [isUserLoading, setIsUserLoading] = React.useState(false);
const [resolvedRoleName, setResolvedRoleName] = React.useState<string | null>(
null,
);
const [isRoleLoading, setIsRoleLoading] = React.useState(false);
React.useEffect(() => {
let isActive = true;
if (!user) {
setResolvedUserName(null);
setIsUserLoading(false);
return () => {
isActive = false;
};
}
setIsUserLoading(true);
getCachedUserName(user)
.then((name) => {
if (!isActive) {
return;
}
setResolvedUserName(name);
})
.finally(() => {
if (!isActive) {
return;
}
setIsUserLoading(false);
});
return () => {
isActive = false;
};
}, [user?.id, user?.guid]);
React.useEffect(() => {
let isActive = true;
if (!role) {
setResolvedRoleName(null);
setIsRoleLoading(false);
return () => {
isActive = false;
};
}
setIsRoleLoading(true);
getCachedRoleName(role)
.then((name) => {
if (!isActive) {
return;
}
setResolvedRoleName(name);
})
.finally(() => {
if (!isActive) {
return;
}
setIsRoleLoading(false);
});
return () => {
isActive = false;
};
}, [role?.id, role?.guid]);
if (user) {
if (isUserLoading) {
return <span>{t("Loading")}...</span>;
}
if (resolvedUserName) {
return <span>{resolvedUserName}</span>;
}
const userFallback = user.guid ?? user.id?.toString();
return <span>{userFallback}</span>;
}
if (role) {
if (isRoleLoading) {
return <span>{t("Loading")}...</span>;
}
if (resolvedRoleName) {
return <span>{resolvedRoleName}</span>;
}
const roleFallback = role.guid ?? role.id?.toString();
return <span>{roleFallback}</span>;
}
return <span>{t("Unassigned")}</span>;
};
export default AssigneePanel;

View File

@ -7,6 +7,7 @@ import Table, {
} from "../../../../components/common/Table";
import { GetMyAssignments } from "../services/tasksService";
import TaskTypeAndNameDisplayPanel from "../../workflowTemplates/components/TaskTypeAndNameDisplayPanel";
import AssigneePanel from "./AssigneePanel";
export interface TasksTableProps extends PublishedTableProps<GetMyAssignments> {}
@ -38,8 +39,13 @@ const TasksTable: React.FC<TasksTableProps> = ({
},
order: "asc",
},
{ key: "user", label: t("User"), order: "asc" },
{ key: "role", label: t("Role"), order: "asc" },
{
key: "user",
label: t("Assignee"),
order: "asc",
searchable: false,
content: (item) => <AssigneePanel user={item.user} role={item.role} />,
},
{ key: "startDateTime", label: t("StartDateTime"), order: "asc" },
],
[t],