fix: replaced first name, last name or email to display name (#1796)

* fix: replacing first, last name and email with display name

* fix: different endpoint for workspace & project member

* fix: falling back to email if display_name doesn't exist
This commit is contained in:
Dakshesh Jain 2023-08-08 13:01:43 +05:30 committed by GitHub
parent cf306ee605
commit 981acc81c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 333 additions and 419 deletions

View File

@ -16,15 +16,7 @@ export const CustomTooltip: React.FC<Props> = ({ datum, analytics, params }) =>
if (params.segment) {
if (DATE_KEYS.includes(params.segment)) tooltipValue = renderMonthAndYear(datum.id);
else if (params.segment === "assignees__email") {
const assignee = analytics.extras.assignee_details.find(
(a) => a.assignees__email === datum.id
);
if (assignee)
tooltipValue = assignee.assignees__first_name + " " + assignee.assignees__last_name;
else tooltipValue = "No assignees";
} else tooltipValue = datum.id;
else tooltipValue = datum.id;
} else {
if (DATE_KEYS.includes(params.x_axis)) tooltipValue = datum.indexValue;
else tooltipValue = datum.id === "count" ? "Issue count" : "Estimate";

View File

@ -70,17 +70,17 @@ export const AnalyticsGraph: React.FC<Props> = ({
height={fullScreen ? "400px" : "300px"}
margin={{
right: 20,
bottom: params.x_axis === "assignees__email" ? 50 : longestXAxisLabel.length * 5 + 20,
bottom: params.x_axis === "assignees__id" ? 50 : longestXAxisLabel.length * 5 + 20,
}}
axisBottom={{
tickSize: 0,
tickPadding: 10,
tickRotation: barGraphData.data.length > 7 ? -45 : 0,
renderTick:
params.x_axis === "assignees__email"
params.x_axis === "assignees__id"
? (datum) => {
const avatar = analytics.extras.assignee_details?.find(
(a) => a?.assignees__email === datum?.value
(a) => a?.assignees__display_name === datum?.value
)?.assignees__avatar;
if (avatar && avatar !== "")

View File

@ -277,9 +277,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
<div className="space-y-4 mt-4">
<div className="flex items-center gap-2 text-xs">
<h6 className="text-custom-text-200">Lead</h6>
<span>
{cycleDetails.owned_by?.first_name} {cycleDetails.owned_by?.last_name}
</span>
<span>{cycleDetails.owned_by?.display_name}</span>
</div>
<div className="flex items-center gap-2 text-xs">
<h6 className="text-custom-text-200">Start Date</h6>
@ -305,10 +303,7 @@ export const AnalyticsSidebar: React.FC<Props> = ({
<div className="space-y-4 mt-4">
<div className="flex items-center gap-2 text-xs">
<h6 className="text-custom-text-200">Lead</h6>
<span>
{moduleDetails.lead_detail?.first_name}{" "}
{moduleDetails.lead_detail?.last_name}
</span>
<span>{moduleDetails.lead_detail?.display_name}</span>
</div>
<div className="flex items-center gap-2 text-xs">
<h6 className="text-custom-text-200">Start Date</h6>

View File

@ -21,115 +21,96 @@ type Props = {
yAxisKey: "count" | "estimate";
};
export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, params, yAxisKey }) => {
const renderAssigneeName = (email: string): string => {
const assignee = analytics.extras.assignee_details.find((a) => a.assignees__email === email);
if (!assignee) return "No assignee";
if (assignee.assignees__first_name !== "")
return assignee.assignees__first_name + " " + assignee.assignees__last_name;
return email;
};
return (
<div className="flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full align-middle">
<table className="min-w-full divide-y divide-custom-border-200 whitespace-nowrap border-y border-custom-border-200">
<thead className="bg-custom-background-80">
<tr className="divide-x divide-custom-border-200 text-sm text-custom-text-100">
<th scope="col" className="py-3 px-2.5 text-left font-medium">
{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === params.x_axis)?.label}
</th>
{params.segment ? (
barGraphData.xAxisKeys.map((key) => (
<th
key={`segment-${key}`}
scope="col"
className={`px-2.5 py-3 text-left font-medium ${
params.segment === "priority" || params.segment === "state__group"
? "capitalize"
: ""
}`}
>
<div className="flex items-center gap-2">
{params.segment === "priority" ? (
getPriorityIcon(key)
) : (
<span
className="h-3 w-3 flex-shrink-0 rounded"
style={{
backgroundColor: generateBarColor(key, analytics, params, "segment"),
}}
/>
)}
{DATE_KEYS.includes(params.segment ?? "")
? renderMonthAndYear(key)
: params.segment === "assignees__email"
? renderAssigneeName(key)
: key}
</div>
</th>
))
) : (
<th scope="col" className="py-3 px-2.5 text-left font-medium sm:pr-0">
{ANALYTICS_Y_AXIS_VALUES.find((v) => v.value === params.y_axis)?.label}
</th>
)}
</tr>
</thead>
<tbody className="divide-y divide-custom-border-200">
{barGraphData.data.map((item, index) => (
<tr
key={`table-row-${index}`}
className="divide-x divide-custom-border-200 text-xs text-custom-text-200"
>
<td
className={`flex items-center gap-2 whitespace-nowrap py-2 px-2.5 font-medium ${
params.x_axis === "priority" || params.x_axis === "state__group"
export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, params, yAxisKey }) => (
<div className="flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full align-middle">
<table className="min-w-full divide-y divide-custom-border-200 whitespace-nowrap border-y border-custom-border-200">
<thead className="bg-custom-background-80">
<tr className="divide-x divide-custom-border-200 text-sm text-custom-text-100">
<th scope="col" className="py-3 px-2.5 text-left font-medium">
{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === params.x_axis)?.label}
</th>
{params.segment ? (
barGraphData.xAxisKeys.map((key) => (
<th
key={`segment-${key}`}
scope="col"
className={`px-2.5 py-3 text-left font-medium ${
params.segment === "priority" || params.segment === "state__group"
? "capitalize"
: ""
}`}
>
{params.x_axis === "priority" ? (
getPriorityIcon(`${item.name}`)
) : (
<span
className="h-3 w-3 rounded"
style={{
backgroundColor: generateBarColor(
`${item.name}`,
analytics,
params,
"x_axis"
),
}}
/>
)}
{params.x_axis === "assignees__email"
? renderAssigneeName(`${item.name}`)
: addSpaceIfCamelCase(`${item.name}`)}
</td>
{params.segment ? (
barGraphData.xAxisKeys.map((key, index) => (
<td
key={`segment-value-${index}`}
className="whitespace-nowrap py-2 px-2.5 sm:pr-0"
>
{item[key] ?? 0}
</td>
))
<div className="flex items-center gap-2">
{params.segment === "priority" ? (
getPriorityIcon(key)
) : (
<span
className="h-3 w-3 flex-shrink-0 rounded"
style={{
backgroundColor: generateBarColor(key, analytics, params, "segment"),
}}
/>
)}
{DATE_KEYS.includes(params.segment ?? "") ? renderMonthAndYear(key) : key}
</div>
</th>
))
) : (
<th scope="col" className="py-3 px-2.5 text-left font-medium sm:pr-0">
{ANALYTICS_Y_AXIS_VALUES.find((v) => v.value === params.y_axis)?.label}
</th>
)}
</tr>
</thead>
<tbody className="divide-y divide-custom-border-200">
{barGraphData.data.map((item, index) => (
<tr
key={`table-row-${index}`}
className="divide-x divide-custom-border-200 text-xs text-custom-text-200"
>
<td
className={`flex items-center gap-2 whitespace-nowrap py-2 px-2.5 font-medium ${
params.x_axis === "priority" || params.x_axis === "state__group"
? "capitalize"
: ""
}`}
>
{params.x_axis === "priority" ? (
getPriorityIcon(`${item.name}`)
) : (
<td className="whitespace-nowrap py-2 px-2.5 sm:pr-0">{item[yAxisKey]}</td>
<span
className="h-3 w-3 rounded"
style={{
backgroundColor: generateBarColor(
`${item.name}`,
analytics,
params,
"x_axis"
),
}}
/>
)}
</tr>
))}
</tbody>
</table>
</div>
{addSpaceIfCamelCase(`${item.name}`)}
</td>
{params.segment ? (
barGraphData.xAxisKeys.map((key, index) => (
<td
key={`segment-value-${index}`}
className="whitespace-nowrap py-2 px-2.5 sm:pr-0"
>
{item[key] ?? 0}
</td>
))
) : (
<td className="whitespace-nowrap py-2 px-2.5 sm:pr-0">{item[yAxisKey]}</td>
)}
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
</div>
);

View File

@ -1,7 +1,7 @@
type Props = {
users: {
avatar: string | null;
email: string | null;
display_name: string | null;
firstName: string;
lastName: string;
count: number;
@ -16,7 +16,7 @@ export const AnalyticsLeaderboard: React.FC<Props> = ({ users, title }) => (
<div className="mt-3 space-y-3">
{users.map((user) => (
<div
key={user.email ?? "None"}
key={user.display_name ?? "None"}
className="flex items-start justify-between gap-4 text-xs"
>
<div className="flex items-center gap-2">
@ -25,16 +25,16 @@ export const AnalyticsLeaderboard: React.FC<Props> = ({ users, title }) => (
<img
src={user.avatar}
className="absolute top-0 left-0 h-full w-full object-cover rounded-full"
alt={user.email ?? "None"}
alt={user.display_name ?? "None"}
/>
</div>
) : (
<div className="grid place-items-center flex-shrink-0 rounded-full bg-gray-700 text-[11px] capitalize text-white h-4 w-4">
{user.firstName !== "" ? user.firstName[0] : "?"}
{user.display_name !== "" ? user?.display_name?.[0] : "?"}
</div>
)}
<span className="break-words text-custom-text-200">
{user.firstName !== "" ? `${user.firstName} ${user.lastName}` : "No assignee"}
{user.display_name !== "" ? `${user.display_name}` : "No assignee"}
</span>
</div>
<span className="flex-shrink-0">{user.count}</span>

View File

@ -56,9 +56,9 @@ export const ScopeAndDemand: React.FC<Props> = ({ fullScreen = true }) => {
<AnalyticsLeaderboard
users={defaultAnalytics.most_issue_created_user?.map((user) => ({
avatar: user?.created_by__avatar,
email: user?.created_by__email,
firstName: user?.created_by__first_name,
lastName: user?.created_by__last_name,
display_name: user?.created_by__display_name,
count: user?.count,
}))}
title="Most issues created"
@ -66,9 +66,9 @@ export const ScopeAndDemand: React.FC<Props> = ({ fullScreen = true }) => {
<AnalyticsLeaderboard
users={defaultAnalytics.most_issue_closed_user?.map((user) => ({
avatar: user?.assignees__avatar,
email: user?.assignees__email,
firstName: user?.assignees__first_name,
lastName: user?.assignees__last_name,
display_name: user?.assignees__display_name,
count: user?.count,
}))}
title="Most issues closed"

View File

@ -16,23 +16,20 @@ export const AnalyticsScope: React.FC<Props> = ({ defaultAnalytics }) => (
{defaultAnalytics.pending_issue_user.length > 0 ? (
<BarGraph
data={defaultAnalytics.pending_issue_user}
indexBy="assignees__email"
indexBy="assignees__display_name"
keys={["count"]}
height="250px"
colors={() => `#f97316`}
customYAxisTickValues={defaultAnalytics.pending_issue_user.map((d) => d.count)}
tooltip={(datum) => {
const assignee = defaultAnalytics.pending_issue_user.find(
(a) => a.assignees__email === `${datum.indexValue}`
(a) => a.assignees__display_name === `${datum.indexValue}`
);
return (
<div className="rounded-md border border-custom-border-200 bg-custom-background-80 p-2 text-xs">
<span className="font-medium text-custom-text-200">
{assignee
? assignee.assignees__first_name + " " + assignee.assignees__last_name
: "No assignee"}
:{" "}
{assignee ? assignee.assignees__display_name : "No assignee"}:{" "}
</span>
{datum.value}
</div>

View File

@ -34,15 +34,12 @@ export const ChangeIssueAssignee: React.FC<Props> = ({ setIsPaletteOpen, issue,
const options =
members?.map(({ member }) => ({
value: member.id,
query:
(member.first_name && member.first_name !== "" ? member.first_name : member.email) +
" " +
member.last_name ?? "",
query: member.display_name,
content: (
<>
<div className="flex items-center gap-2">
<Avatar user={member} />
{member.first_name && member.first_name !== "" ? member.first_name : member.email}
{member.display_name}
</div>
{issue.assignees.includes(member.id) && (
<div>

View File

@ -157,10 +157,10 @@ export const FiltersList: React.FC<Props> = ({
return (
<div
key={memberId}
className="inline-flex items-center gap-x-1 rounded-full bg-custom-background-90 px-1 capitalize"
className="inline-flex items-center gap-x-1 rounded-full bg-custom-background-90 px-1"
>
<Avatar user={member} />
<span>{member?.first_name}</span>
<span>{member?.display_name}</span>
<span
className="cursor-pointer"
onClick={() =>
@ -184,7 +184,7 @@ export const FiltersList: React.FC<Props> = ({
className="inline-flex items-center gap-x-1 rounded-full bg-custom-background-90 px-1 capitalize"
>
<Avatar user={member} />
<span>{member?.first_name}</span>
<span>{member?.display_name}</span>
<span
className="cursor-pointer"
onClick={() =>

View File

@ -62,7 +62,7 @@ export const LinksList: React.FC<Props> = ({ links, handleDeleteLink, userAuth }
by{" "}
{link.created_by_detail.is_bot
? link.created_by_detail.first_name + " Bot"
: link.created_by_detail.email}
: link.created_by_detail.display_name}
</p>
</div>
</a>

View File

@ -133,9 +133,10 @@ export const SidebarProgressStats: React.FC<Props> = ({
avatar: assignee.avatar ?? "",
first_name: assignee.first_name ?? "",
last_name: assignee.last_name ?? "",
display_name: assignee.display_name ?? "",
}}
/>
<span>{assignee.first_name}</span>
<span>{assignee.display_name}</span>
</div>
}
completed={assignee.completed_issues}

View File

@ -81,10 +81,7 @@ export const BoardHeader: React.FC<Props> = ({
break;
case "created_by":
const member = members?.find((member) => member.member.id === groupTitle)?.member;
title =
member?.first_name && member.first_name !== ""
? `${member.first_name} ${member.last_name}`
: member?.email ?? "";
title = member?.display_name ?? "";
break;
}
@ -149,7 +146,9 @@ export const BoardHeader: React.FC<Props> = ({
>
<span className="flex items-center">{getGroupIcon()}</span>
<h2
className="text-lg font-semibold capitalize truncate"
className={`text-lg font-semibold truncate ${
selectedGroup === "created_by" ? "" : "capitalize"
}`}
style={{
writingMode: isCollapsed ? "horizontal-tb" : "vertical-rl",
}}

View File

@ -96,10 +96,7 @@ export const SingleList: React.FC<Props> = ({
break;
case "created_by":
const member = members?.find((member) => member.member.id === groupTitle)?.member;
title =
member?.first_name && member.first_name !== ""
? `${member.first_name} ${member.last_name}`
: member?.email ?? "";
title = member?.display_name ?? "";
break;
}
@ -163,7 +160,11 @@ export const SingleList: React.FC<Props> = ({
<div className="flex items-center">{getGroupIcon()}</div>
)}
{selectedGroup !== null ? (
<h2 className="text-sm font-semibold capitalize leading-6 text-custom-text-100">
<h2
className={`text-sm font-semibold leading-6 text-custom-text-100 ${
selectedGroup === "created_by" ? "" : "capitalize"
}`}
>
{getGroupTitle()}
</h2>
) : (

View File

@ -361,14 +361,14 @@ export const ActiveCycleDetails: React.FC = () => {
height={16}
width={16}
className="rounded-full"
alt={cycle.owned_by.first_name}
alt={cycle.owned_by.display_name}
/>
) : (
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-custom-background-100 capitalize">
{cycle.owned_by.first_name.charAt(0)}
{cycle.owned_by.display_name.charAt(0)}
</span>
)}
<span className="text-custom-text-200">{cycle.owned_by.first_name}</span>
<span className="text-custom-text-200">{cycle.owned_by.display_name}</span>
</div>
{cycle.assignees.length > 0 && (

View File

@ -88,9 +88,10 @@ export const ActiveCycleProgressStats: React.FC<Props> = ({ cycle }) => {
avatar: assignee.avatar ?? "",
first_name: assignee.first_name ?? "",
last_name: assignee.last_name ?? "",
display_name: assignee.display_name ?? "",
}}
/>
<span>{assignee.first_name}</span>
<span>{assignee.display_name}</span>
</div>
}
completed={assignee.completed_issues}

View File

@ -450,14 +450,14 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
height={12}
width={12}
className="rounded-full"
alt={cycle.owned_by.first_name}
alt={cycle.owned_by.display_name}
/>
) : (
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-gray-800 capitalize text-white">
{cycle.owned_by.first_name.charAt(0)}
{cycle.owned_by.display_name.charAt(0)}
</span>
)}
<span className="text-custom-text-200">{cycle.owned_by.first_name}</span>
<span className="text-custom-text-200">{cycle.owned_by.display_name}</span>
</div>
</div>

View File

@ -250,14 +250,14 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
height={16}
width={16}
className="rounded-full"
alt={cycle.owned_by.first_name}
alt={cycle.owned_by.display_name}
/>
) : (
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-orange-300 capitalize text-white">
{cycle.owned_by.first_name.charAt(0)}
{cycle.owned_by.display_name.charAt(0)}
</span>
)}
<span className="text-custom-text-200">{cycle.owned_by.first_name}</span>
<span className="text-custom-text-200">{cycle.owned_by.display_name}</span>
</div>
</div>
<div className="flex h-5 items-center gap-2">

View File

@ -254,11 +254,11 @@ export const SingleCycleList: React.FC<TSingleStatProps> = ({
height={16}
width={16}
className="rounded-full"
alt={cycle.owned_by.first_name}
alt={cycle.owned_by.display_name}
/>
) : (
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-orange-300 capitalize text-white">
{cycle.owned_by.first_name.charAt(0)}
{cycle.owned_by.display_name.charAt(0)}
</span>
)}
</div>

View File

@ -44,19 +44,12 @@ export const SingleUserSelect: React.FC<Props> = ({ collaborator, index, users,
);
const options = members?.map((member) => ({
value: member.member.email,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
value: member.member.display_name,
query: member.member.display_name ?? "",
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name + "(" + member.member.email + ")"
: member.member.email}
{member.member.display_name}
</div>
),
}));

View File

@ -34,18 +34,11 @@ export const JiraImportUsers: FC = () => {
const options = members?.map((member) => ({
value: member.member.email,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name ?? "",
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name + " (" + member.member.email + ")"
: member.member.email}
{member.member.display_name}
</div>
),
}));

View File

@ -42,12 +42,7 @@ export const SingleImport: React.FC<Props> = ({ service, refreshing, handleDelet
</h4>
<div className="mt-2 flex items-center gap-2 text-xs text-custom-text-200">
<span>{renderShortDateWithYearFormat(service.created_at)}</span>|
<span>
Imported by{" "}
{service.initiated_by_detail.first_name && service.initiated_by_detail.first_name !== ""
? service.initiated_by_detail.first_name + " " + service.initiated_by_detail.last_name
: service.initiated_by_detail.email}
</span>
<span>Imported by {service.initiated_by_detail.display_name}</span>
</div>
</div>
<CustomMenu ellipsis>

View File

@ -122,7 +122,7 @@ export const IssueActivitySection: React.FC<Props> = ({ issueId, user }) => {
activityItem.actor_detail.avatar !== "" ? (
<img
src={activityItem.actor_detail.avatar}
alt={activityItem.actor_detail.first_name}
alt={activityItem.actor_detail.display_name}
height={24}
width={24}
className="rounded-full"
@ -131,7 +131,7 @@ export const IssueActivitySection: React.FC<Props> = ({ issueId, user }) => {
<div
className={`grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-700 text-xs text-white`}
>
{activityItem.actor_detail.first_name.charAt(0)}
{activityItem.actor_detail.display_name.charAt(0)}
</div>
)}
</div>
@ -150,8 +150,7 @@ export const IssueActivitySection: React.FC<Props> = ({ issueId, user }) => {
) : (
<Link href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}>
<a className="text-gray font-medium">
{activityItem.actor_detail.first_name}{" "}
{activityItem.actor_detail.last_name}
{activityItem.actor_detail.display_name}
</a>
</Link>
)}{" "}

View File

@ -77,7 +77,7 @@ export const IssueAttachments = () => {
<Tooltip
tooltipContent={`${
people?.find((person) => person.member.id === file.updated_by)?.member
.first_name ?? ""
.display_name ?? ""
} uploaded on ${renderLongDateFormat(file.updated_at)}`}
>
<span>

View File

@ -70,7 +70,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
{comment.actor_detail.avatar && comment.actor_detail.avatar !== "" ? (
<img
src={comment.actor_detail.avatar}
alt={comment.actor_detail.first_name}
alt={comment.actor_detail.display_name}
height={30}
width={30}
className="grid h-7 w-7 place-items-center rounded-full border-2 border-custom-border-200"
@ -79,7 +79,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
<div
className={`grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-500 text-white`}
>
{comment.actor_detail.first_name.charAt(0)}
{comment.actor_detail.display_name.charAt(0)}
</div>
)}
@ -93,8 +93,9 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
<div className="min-w-0 flex-1">
<div>
<div className="text-xs">
{comment.actor_detail.first_name}
{comment.actor_detail.is_bot ? "Bot" : " " + comment.actor_detail.last_name}
{comment.actor_detail.is_bot
? comment.actor_detail.first_name + " Bot"
: comment.actor_detail.display_name}
</div>
<p className="mt-0.5 text-xs text-custom-text-200">
Commented {timeAgo(comment.created_at)}

View File

@ -30,20 +30,11 @@ export const IssueAssigneeSelect: React.FC<Props> = ({ projectId, value = [], on
const options = members?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name ?? "",
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{`${
member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email
} ${member.member.last_name ?? ""}`}
{member.member.display_name}
</div>
),
}));

View File

@ -41,20 +41,11 @@ export const SidebarAssigneeSelect: React.FC<Props> = ({
const options = members?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{`${
member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email
} ${member.member.last_name ?? ""}`}
{member.member.display_name}
</div>
),
}));

View File

@ -47,20 +47,11 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
const options = members?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{`${
member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email
} ${member.member.last_name ?? ""}`}
{member.member.display_name}
</div>
),
}));
@ -71,11 +62,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
tooltipHeading="Assignees"
tooltipContent={
issue.assignee_details.length > 0
? issue.assignee_details
.map((assignee) =>
assignee?.first_name !== "" ? assignee?.first_name : assignee?.email
)
.join(", ")
? issue.assignee_details.map((assignee) => assignee?.display_name).join(", ")
: "No Assignee"
}
>

View File

@ -32,18 +32,11 @@ export const ModuleLeadSelect: React.FC<Props> = ({ value, onChange }) => {
const options = members?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
{member.member.display_name}
</div>
),
}));
@ -62,11 +55,7 @@ export const ModuleLeadSelect: React.FC<Props> = ({ value, onChange }) => {
<UserCircleIcon className="h-4 w-4 text-custom-text-200" />
)}
{selectedOption ? (
selectedOption?.first_name && selectedOption.first_name !== "" ? (
selectedOption?.first_name
) : (
selectedOption?.email
)
selectedOption?.display_name
) : (
<span className="text-custom-text-200">Lead</span>
)}

View File

@ -30,18 +30,11 @@ export const ModuleMembersSelect: React.FC<Props> = ({ value, onChange }) => {
);
const options = members?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
{member.member.display_name}
</div>
),
}));

View File

@ -33,18 +33,11 @@ export const SidebarLeadSelect: React.FC<Props> = ({ value, onChange }) => {
const options = members?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
{member.member.display_name}
</div>
),
}));
@ -64,11 +57,7 @@ export const SidebarLeadSelect: React.FC<Props> = ({ value, onChange }) => {
<div className="flex items-center gap-2">
{selectedOption && <Avatar user={selectedOption} />}
{selectedOption ? (
selectedOption?.first_name && selectedOption.first_name !== "" ? (
selectedOption?.first_name
) : (
selectedOption?.email
)
selectedOption?.display_name
) : (
<span className="text-custom-text-200">No lead</span>
)}

View File

@ -31,18 +31,11 @@ export const SidebarMembersSelect: React.FC<Props> = ({ value, onChange }) => {
const options = members?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
{member.member.display_name}
</div>
),
}));

View File

@ -78,8 +78,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
) : (
<div className="w-12 h-12 bg-custom-background-80 rounded-full flex justify-center items-center">
<span className="text-custom-text-100 font-medium text-lg">
{notification.triggered_by_details.first_name?.[0] ? (
notification.triggered_by_details.first_name?.[0]?.toUpperCase()
{notification.triggered_by_details.display_name?.[0] ? (
notification.triggered_by_details.display_name?.[0]?.toUpperCase()
) : (
<Icon iconName="person" className="h-6 w-6" />
)}
@ -89,10 +89,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
</div>
<div className="space-y-2.5 w-full overflow-hidden">
<div className="text-sm w-full break-words">
<span className="font-semibold">
{notification.triggered_by_details.first_name}{" "}
{notification.triggered_by_details.last_name}{" "}
</span>
<span className="font-semibold">{notification.triggered_by_details.display_name} </span>
{notification.data.issue_activity.field !== "comment" &&
notification.data.issue_activity.verb}{" "}
{notification.data.issue_activity.field === "comment"

View File

@ -162,7 +162,7 @@ export const SinglePageDetailedItem: React.FC<TSingleStatProps> = ({
position="top-right"
tooltipContent={`Created by ${
people?.find((person) => person.member.id === page.created_by)?.member
.first_name ?? ""
.display_name ?? ""
} on ${renderLongDateFormat(`${page.created_at}`)}`}
>
<span>

View File

@ -161,7 +161,7 @@ export const SinglePageListItem: React.FC<TSingleStatProps> = ({
position="top-right"
tooltipContent={`Created by ${
people?.find((person) => person.member.id === page.created_by)?.member
.first_name ?? ""
.display_name ?? ""
} on ${renderLongDateFormat(`${page.created_at}`)}`}
>
<span>

View File

@ -38,21 +38,21 @@ export const ProfileActivity = () => {
{activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? (
<img
src={activity.actor_detail.avatar}
alt={activity.actor_detail.first_name}
alt={activity.actor_detail.display_name}
height={24}
width={24}
className="rounded"
/>
) : (
<div className="grid h-6 w-6 place-items-center rounded border-2 bg-gray-700 text-xs text-white">
{activity.actor_detail.first_name.charAt(0)}
{activity.actor_detail.display_name?.charAt(0)}
</div>
)}
</div>
<div className="-mt-1 w-4/5 break-words">
<p className="text-sm text-custom-text-200">
<span className="font-medium text-custom-text-100">
{activity.actor_detail.first_name} {activity.actor_detail.last_name}{" "}
{activity.actor_detail.display_name}{" "}
</span>
{activity.field ? (
<ActivityMessage activity={activity} showIssue />

View File

@ -279,10 +279,10 @@ export const ProfileIssuesView = () => {
dragDisabled={groupByProperty !== "priority"}
emptyState={{
title: router.pathname.includes("assigned")
? `Issues assigned to ${profileData?.user_data.first_name} ${profileData?.user_data.last_name} will appear here`
? `Issues assigned to ${profileData?.user_data.display_name} will appear here`
: router.pathname.includes("created")
? `Issues created by ${profileData?.user_data.first_name} ${profileData?.user_data.last_name} will appear here`
: `Issues subscribed by ${profileData?.user_data.first_name} ${profileData?.user_data.last_name} will appear here`,
? `Issues created by ${profileData?.user_data.display_name} will appear here`
: `Issues subscribed by ${profileData?.user_data.display_name} will appear here`,
}}
handleOnDragEnd={handleOnDragEnd}
handleIssueAction={handleIssueAction}

View File

@ -86,29 +86,29 @@ export const ProfileSidebar = () => {
userProjectsData.user_data.cover_image ??
"https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"
}
alt={userProjectsData.user_data.first_name}
alt={userProjectsData.user_data.display_name}
className="h-32 w-full object-cover"
/>
<div className="absolute -bottom-[26px] left-5 h-[52px] w-[52px] rounded">
{userProjectsData.user_data.avatar && userProjectsData.user_data.avatar !== "" ? (
<img
src={userProjectsData.user_data.avatar}
alt={userProjectsData.user_data.first_name}
alt={userProjectsData.user_data.display_name}
className="rounded"
/>
) : (
<div className="bg-custom-background-90 flex justify-center items-center w-[52px] h-[52px] rounded text-custom-text-100">
{userProjectsData.user_data.first_name[0]}
{userProjectsData.user_data.display_name?.[0]}
</div>
)}
</div>
</div>
<div className="px-5">
<div className="mt-[38px]">
<h4 className="text-lg font-semibold">
{userProjectsData.user_data.first_name} {userProjectsData.user_data.last_name}
</h4>
<h6 className="text-custom-text-200 text-sm">{userProjectsData.user_data.email}</h6>
<h4 className="text-lg font-semibold">{userProjectsData.user_data.display_name}</h4>
<h6 className="text-custom-text-200 text-sm">
{userProjectsData.user_data.display_name}
</h6>
</div>
<div className="mt-6 space-y-5">
{userDetails.map((detail) => (

View File

@ -67,13 +67,13 @@ const ConfirmProjectMemberRemove: React.FC<Props> = ({ isOpen, onClose, data, ha
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Remove {data?.email}?
Remove {data?.display_name}?
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-custom-text-200">
Are you sure you want to remove member-{" "}
<span className="font-bold">{data?.email}</span>? They will no longer have
access to this project. This action cannot be undone.
<span className="font-bold">{data?.display_name}</span>? They will no
longer have access to this project. This action cannot be undone.
</p>
</div>
</div>

View File

@ -163,20 +163,11 @@ export const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen, user })
const options = workspaceMembers?.map((member) => ({
value: member.member.id,
query:
(member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email) +
" " +
member.member.last_name ?? "",
query: member.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{`${
member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email
} ${member.member.last_name ?? ""}`}
{member.member.display_name}
</div>
),
}));
@ -376,10 +367,7 @@ export const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen, user })
{value ? (
<>
<Avatar user={selectedMember?.member} />
<span>
{selectedMember?.member.first_name}{" "}
{selectedMember?.member.last_name}
</span>
<span>{selectedMember?.member.display_name} </span>
<span onClick={() => onChange(null)}>
<Icon
iconName="close"

View File

@ -82,7 +82,7 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
});
const uninvitedPeople = people?.filter((person) => {
const isInvited = members?.find((member) => member.email === person.member.email);
const isInvited = members?.find((member) => member.display_name === person.member.display_name);
return !isInvited;
});
@ -136,11 +136,11 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
const options = uninvitedPeople?.map((person) => ({
value: person.member.id,
query: person.member.email,
query: person.member.display_name,
content: (
<div className="flex items-center gap-2">
<Avatar user={person.member} />
{person.member.email}
{person.member.display_name}
</div>
),
}));
@ -209,7 +209,10 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
people?.find((p) => p.member.id === value)?.member
}
/>
{people?.find((p) => p.member.id === value)?.member.email}
{
people?.find((p) => p.member.id === value)?.member
.display_name
}
</div>
) : (
<div>Select co-worker&rsquo;s email</div>

View File

@ -71,9 +71,7 @@ const SearchListbox: React.FC<Props> = ({
} else
return (
<div className="grid h-4 w-4 flex-shrink-0 place-items-center rounded-full bg-gray-700 capitalize text-white">
{user.member.first_name && user.member.first_name !== ""
? user.member.first_name.charAt(0)
: user.member.email.charAt(0)}
{user.member.display_name.charAt(0)}
</div>
);
};

View File

@ -47,7 +47,7 @@ export const Avatar: React.FC<AvatarProps> = ({
<img
src={user.avatar}
className="absolute top-0 left-0 h-full w-full object-cover rounded-full"
alt={user.first_name}
alt={user.display_name}
/>
</div>
) : (
@ -59,9 +59,7 @@ export const Avatar: React.FC<AvatarProps> = ({
fontSize: fontSize,
}}
>
{user?.first_name && user.first_name !== ""
? user.first_name.charAt(0)
: user?.email?.charAt(0)}
{user?.display_name?.charAt(0)}
</div>
)}
</div>

View File

@ -127,9 +127,7 @@ export const SelectFilters: React.FC<Props> = ({
label: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
{member.member.display_name}
</div>
),
value: {
@ -149,9 +147,7 @@ export const SelectFilters: React.FC<Props> = ({
label: (
<div className="flex items-center gap-2">
<Avatar user={member.member} />
{member.member.first_name && member.member.first_name !== ""
? member.member.first_name
: member.member.email}
{member.member.display_name}
</div>
),
value: {

View File

@ -67,13 +67,13 @@ const ConfirmWorkspaceMemberRemove: React.FC<Props> = ({ isOpen, onClose, data,
as="h3"
className="text-lg font-medium leading-6 text-custom-text-100"
>
Remove {data?.email}?
Remove {data?.display_name}?
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-custom-text-200">
Are you sure you want to remove member-{" "}
<span className="font-bold">{data?.email}</span>? They will no longer have
access to this workspace. This action cannot be undone.
<span className="font-bold">{data?.display_name}</span>? They will no
longer have access to this workspace. This action cannot be undone.
</p>
</div>
</div>

View File

@ -149,7 +149,7 @@ export const WorkspaceSidebarDropdown = () => {
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 shadow-lg outline-none"
>
<div className="flex flex-col items-start justify-start gap-3 p-3">
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div>
<div className="text-sm text-custom-sidebar-text-200">{user?.display_name}</div>
<span className="text-sm font-semibold text-custom-sidebar-text-200">Workspace</span>
{workspaces ? (
<div className="flex h-full w-full flex-col items-start justify-start gap-3.5">

View File

@ -41,8 +41,8 @@ const SingleInvitation: React.FC<Props> = ({
<p className="text-sm text-custom-text-200">
Invited by{" "}
{invitation.created_by_detail
? invitation.created_by_detail.first_name
: invitation.workspace.owner.first_name}
? invitation.created_by_detail.display_name
: invitation.workspace.owner.display_name}
</p>
</div>
<div className="flex-shrink-0 self-center">

View File

@ -19,7 +19,7 @@ export const ANALYTICS_X_AXIS_VALUES: { value: TXAxisValues; label: string }[] =
label: "Label",
},
{
value: "assignees__email",
value: "assignees__id",
label: "Assignee",
},
{

View File

@ -69,9 +69,13 @@ export const WORKSPACE_DETAILS = (workspaceSlug: string) =>
export const WORKSPACE_MEMBERS = (workspaceSlug: string) =>
`WORKSPACE_MEMBERS_${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_MEMBERS_WITH_EMAIL = (workspaceSlug: string) =>
`WORKSPACE_MEMBERS_WITH_EMAIL_${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_MEMBERS_ME = (workspaceSlug: string) =>
`WORKSPACE_MEMBERS_ME${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_INVITATIONS = "WORKSPACE_INVITATIONS";
export const WORKSPACE_INVITATION_WITH_EMAIL = (workspaceSlug: string) =>
`WORKSPACE_INVITATION_WITH_EMAIL_${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_INVITATION = "WORKSPACE_INVITATION";
export const LAST_ACTIVE_WORKSPACE_AND_PROJECTS = "LAST_ACTIVE_WORKSPACE_AND_PROJECTS";
@ -90,7 +94,11 @@ export const PROJECTS_LIST = (
export const PROJECT_DETAILS = (projectId: string) => `PROJECT_DETAILS_${projectId.toUpperCase()}`;
export const PROJECT_MEMBERS = (projectId: string) => `PROJECT_MEMBERS_${projectId.toUpperCase()}`;
export const PROJECT_MEMBERS_WITH_EMAIL = (workspaceSlug: string, projectId: string) =>
`PROJECT_MEMBERS_WITH_EMAIL_${workspaceSlug}_${projectId.toUpperCase()}`;
export const PROJECT_INVITATIONS = "PROJECT_INVITATIONS";
export const PROJECT_INVITATIONS_WITH_EMAIL = (workspaceSlug: string, projectId: string) =>
`PROJECT_INVITATIONS_WITH_EMAIL_${workspaceSlug}_${projectId.toUpperCase()}`;
export const PROJECT_ISSUES_LIST = (workspaceSlug: string, projectId: string) =>
`PROJECT_ISSUES_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}`;

View File

@ -32,8 +32,8 @@ export const SPREADSHEET_COLUMN = [
colName: "Assignees",
colSize: "128px",
icon: UserGroupIcon,
ascendingOrder: "assignees__first_name",
descendingOrder: "-assignees__first_name",
ascendingOrder: "assignees__id",
descendingOrder: "-assignees__id",
},
{
propertyName: "labels",

View File

@ -73,7 +73,7 @@ const ProfileActivity = () => {
activityItem.actor_detail.avatar !== "" ? (
<img
src={activityItem.actor_detail.avatar}
alt={activityItem.actor_detail.first_name}
alt={activityItem.actor_detail.display_name}
height={30}
width={30}
className="grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-500 text-white"
@ -82,7 +82,7 @@ const ProfileActivity = () => {
<div
className={`grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-500 text-white`}
>
{activityItem.actor_detail.first_name.charAt(0)}
{activityItem.actor_detail.display_name?.charAt(0)}
</div>
)}
@ -96,10 +96,9 @@ const ProfileActivity = () => {
<div className="min-w-0 flex-1">
<div>
<div className="text-xs">
{activityItem.actor_detail.first_name}
{activityItem.actor_detail.is_bot
? "Bot"
: " " + activityItem.actor_detail.last_name}
? activityItem.actor_detail.first_name + " Bot"
: activityItem.actor_detail.display_name}
</div>
<p className="mt-0.5 text-xs text-custom-text-200">
Commented {timeAgo(activityItem.created_at)}
@ -176,7 +175,7 @@ const ProfileActivity = () => {
activityItem.actor_detail.avatar !== "" ? (
<img
src={activityItem.actor_detail.avatar}
alt={activityItem.actor_detail.first_name}
alt={activityItem.actor_detail.display_name}
height={24}
width={24}
className="rounded-full"
@ -185,7 +184,7 @@ const ProfileActivity = () => {
<div
className={`grid h-7 w-7 place-items-center rounded-full border-2 border-white bg-gray-700 text-xs text-white`}
>
{activityItem.actor_detail.first_name.charAt(0)}
{activityItem.actor_detail.display_name?.charAt(0)}
</div>
)}
</div>
@ -206,8 +205,7 @@ const ProfileActivity = () => {
href={`/${workspaceSlug}/profile/${activityItem.actor_detail.id}`}
>
<a className="text-gray font-medium">
{activityItem.actor_detail.first_name}{" "}
{activityItem.actor_detail.last_name}
{activityItem.actor_detail.display_name}
</a>
</Link>
)}{" "}

View File

@ -176,7 +176,7 @@ const Profile: NextPage = () => {
src={watch("avatar")}
className="absolute top-0 left-0 h-full w-full object-cover rounded-md"
onClick={() => setIsImageUploadModalOpen(true)}
alt={myProfile.first_name}
alt={myProfile.display_name}
/>
</div>
)}

View File

@ -131,7 +131,7 @@ const ControlSettings: NextPage = () => {
{...field}
label={
people?.find((person) => person.member.id === field.value)?.member
.first_name ?? <span className="text-custom-text-200">Select lead</span>
.display_name ?? <span className="text-custom-text-200">Select lead</span>
}
width="w-full"
input
@ -153,14 +153,10 @@ const ControlSettings: NextPage = () => {
</div>
) : (
<div className="grid h-4 w-4 flex-shrink-0 place-items-center rounded-full bg-gray-700 capitalize text-white">
{person.member.first_name && person.member.first_name !== ""
? person.member.first_name.charAt(0)
: person.member.email.charAt(0)}
{person.member.display_name?.charAt(0)}
</div>
)}
{person.member.first_name !== ""
? person.member.first_name
: person.member.email}
{person.member.display_name}
</div>
</CustomSelect.Option>
))}
@ -190,7 +186,7 @@ const ControlSettings: NextPage = () => {
<CustomSelect
{...field}
label={
people?.find((p) => p.member.id === field.value)?.member.first_name ?? (
people?.find((p) => p.member.id === field.value)?.member.display_name ?? (
<span className="text-custom-text-200">Select default assignee</span>
)
}
@ -214,14 +210,10 @@ const ControlSettings: NextPage = () => {
</div>
) : (
<div className="grid h-4 w-4 flex-shrink-0 place-items-center rounded-full bg-gray-700 capitalize text-white">
{person.member.first_name && person.member.first_name !== ""
? person.member.first_name.charAt(0)
: person.member.email.charAt(0)}
{person.member.display_name?.charAt(0)}
</div>
)}
{person.member.first_name !== ""
? person.member.first_name
: person.member.email}
{person.member.display_name}
</div>
</CustomSelect.Option>
))}

View File

@ -26,7 +26,11 @@ import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline";
// types
import type { NextPage } from "next";
// fetch-keys
import { PROJECT_INVITATIONS, PROJECT_MEMBERS, WORKSPACE_DETAILS } from "constants/fetch-keys";
import {
PROJECT_INVITATIONS_WITH_EMAIL,
PROJECT_MEMBERS_WITH_EMAIL,
WORKSPACE_DETAILS,
} from "constants/fetch-keys";
// constants
import { ROLE } from "constants/workspace";
// helper
@ -51,16 +55,21 @@ const MembersSettings: NextPage = () => {
);
const { data: projectMembers, mutate: mutateMembers } = useSWR(
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
workspaceSlug && projectId
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
? PROJECT_MEMBERS_WITH_EMAIL(workspaceSlug.toString(), projectId.toString())
: null,
workspaceSlug && projectId
? () => projectService.projectMembersWithEmail(workspaceSlug as string, projectId as string)
: null
);
const { data: projectInvitations, mutate: mutateInvitations } = useSWR(
workspaceSlug && projectId ? PROJECT_INVITATIONS : null,
workspaceSlug && projectId
? () => projectService.projectInvitations(workspaceSlug as string, projectId as string)
? PROJECT_INVITATIONS_WITH_EMAIL(workspaceSlug.toString(), projectId.toString())
: null,
workspaceSlug && projectId
? () =>
projectService.projectInvitationsWithEmail(workspaceSlug as string, projectId as string)
: null
);
@ -71,7 +80,7 @@ const MembersSettings: NextPage = () => {
avatar: item.member?.avatar,
first_name: item.member?.first_name,
last_name: item.member?.last_name,
email: item.member?.email,
display_name: item.member?.display_name,
role: item.role,
status: true,
member: true,
@ -82,7 +91,7 @@ const MembersSettings: NextPage = () => {
avatar: item.avatar ?? "",
first_name: item.first_name ?? item.email,
last_name: item.last_name ?? "",
email: item.email,
display_name: item.email,
role: item.role,
status: item.accepted,
member: false,
@ -181,28 +190,24 @@ const MembersSettings: NextPage = () => {
{member.avatar && member.avatar !== "" ? (
<img
src={member.avatar}
alt={member.first_name}
alt={member.display_name}
className="absolute top-0 left-0 h-full w-full object-cover rounded-lg"
/>
) : member.first_name !== "" ? (
member.first_name.charAt(0)
) : (
member.email.charAt(0)
member.display_name.charAt(0)
)}
</div>
<div>
{member.member ? (
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
<a className="text-sm">
{member.first_name} {member.last_name}
</a>
<a className="text-sm">{member.display_name}</a>
</Link>
) : (
<h4 className="text-sm">
{member.first_name} {member.last_name}
</h4>
<h4 className="text-sm">{member.display_name}</h4>
)}
<p className="mt-0.5 text-xs text-custom-text-200">{member.email}</p>
<p className="mt-0.5 text-xs text-custom-text-200">
{member.display_name}
</p>
</div>
</div>
<div className="flex items-center gap-2 text-xs">

View File

@ -24,7 +24,11 @@ import { PlusIcon } from "@heroicons/react/24/outline";
// types
import type { NextPage } from "next";
// fetch-keys
import { WORKSPACE_DETAILS, WORKSPACE_INVITATIONS, WORKSPACE_MEMBERS } from "constants/fetch-keys";
import {
WORKSPACE_DETAILS,
WORKSPACE_INVITATION_WITH_EMAIL,
WORKSPACE_MEMBERS_WITH_EMAIL,
} from "constants/fetch-keys";
// constants
import { ROLE } from "constants/workspace";
// helper
@ -48,13 +52,17 @@ const MembersSettings: NextPage = () => {
);
const { data: workspaceMembers, mutate: mutateMembers } = useSWR(
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug.toString()) : null,
workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug.toString()) : null
workspaceSlug ? WORKSPACE_MEMBERS_WITH_EMAIL(workspaceSlug.toString()) : null,
workspaceSlug
? () => workspaceService.workspaceMembersWithEmail(workspaceSlug.toString())
: null
);
const { data: workspaceInvitations, mutate: mutateInvitations } = useSWR(
workspaceSlug ? WORKSPACE_INVITATIONS : null,
workspaceSlug ? () => workspaceService.workspaceInvitations(workspaceSlug.toString()) : null
workspaceSlug ? WORKSPACE_INVITATION_WITH_EMAIL(workspaceSlug.toString()) : null,
workspaceSlug
? () => workspaceService.workspaceInvitationsWithEmail(workspaceSlug.toString())
: null
);
const members = [
@ -65,6 +73,7 @@ const MembersSettings: NextPage = () => {
first_name: item.member?.first_name,
last_name: item.member?.last_name,
email: item.member?.email,
display_name: item.member?.display_name,
role: item.role,
status: true,
member: true,
@ -77,6 +86,7 @@ const MembersSettings: NextPage = () => {
first_name: item.email,
last_name: "",
email: item.email,
display_name: item.email,
role: item.role,
status: item.accepted,
member: false,
@ -199,27 +209,23 @@ const MembersSettings: NextPage = () => {
<img
src={member.avatar}
className="absolute top-0 left-0 h-full w-full object-cover rounded-lg"
alt={member.first_name}
alt={member.display_name || member.email}
/>
) : member.first_name !== "" ? (
member.first_name.charAt(0)
) : (
member.email.charAt(0)
(member.display_name || member.email).charAt(0)
)}
</div>
<div>
{member.member ? (
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
<a className="text-sm">
{member.first_name} {member.last_name}
</a>
<a className="text-sm">{member.display_name || member.email}</a>
</Link>
) : (
<h4 className="text-sm">
{member.first_name} {member.last_name}
</h4>
<h4 className="text-sm">{member.display_name}</h4>
)}
<p className="text-xs text-custom-text-200">{member.email}</p>
<p className="text-xs text-custom-text-200">
{member.display_name || member.email}
</p>
</div>
</div>
<div className="flex items-center gap-2 text-xs">

View File

@ -26,6 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
email: user.email,
first_name: user.first_name,
last_name: user.last_name,
display_name: user?.display_name,
})
.then(() => {
jitsu.track(eventName, {

View File

@ -151,6 +151,17 @@ class ProjectServices extends APIService {
}
async projectMembers(workspaceSlug: string, projectId: string): Promise<IProjectMember[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/project-members/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async projectMembersWithEmail(
workspaceSlug: string,
projectId: string
): Promise<IProjectMember[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/members/`)
.then((response) => response?.data)
.catch((error) => {
@ -219,6 +230,17 @@ class ProjectServices extends APIService {
});
}
async projectInvitationsWithEmail(
workspaceSlug: string,
projectId: string
): Promise<IProjectMemberInvitation[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/invitations/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async updateProjectInvitation(
workspaceSlug: string,
projectId: string,

View File

@ -155,6 +155,14 @@ class WorkspaceService extends APIService {
}
async workspaceMembers(workspaceSlug: string): Promise<IWorkspaceMember[]> {
return this.get(`/api/workspaces/${workspaceSlug}/workspace-members/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async workspaceMembersWithEmail(workspaceSlug: string): Promise<IWorkspaceMember[]> {
return this.get(`/api/workspaces/${workspaceSlug}/members/`)
.then((response) => response?.data)
.catch((error) => {
@ -209,6 +217,16 @@ class WorkspaceService extends APIService {
});
}
async workspaceInvitationsWithEmail(
workspaceSlug: string
): Promise<IWorkspaceMemberInvitation[]> {
return this.get(`/api/workspaces/${workspaceSlug}/invitations/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getWorkspaceInvitation(invitationId: string): Promise<IWorkspaceMemberInvitation> {
return this.get(`/api/users/me/invitations/${invitationId}/`, { headers: {} })
.then((response) => response?.data)

View File

@ -4,8 +4,8 @@ export interface IAnalyticsResponse {
extras: {
colors: IAnalyticsExtra[];
assignee_details: {
assignees__display_name: string | null;
assignees__avatar: string | null;
assignees__email: string;
assignees__first_name: string;
assignees__last_name: string;
}[];
@ -30,7 +30,7 @@ export type TXAxisValues =
| "state__name"
| "state__group"
| "labels__name"
| "assignees__email"
| "assignees__id"
| "estimate_point"
| "issue_cycle__cycle__name"
| "issue_module__module__name"
@ -65,9 +65,9 @@ export interface IExportAnalyticsFormData {
export interface IDefaultAnalyticsUser {
assignees__avatar: string | null;
assignees__email: string | null;
assignees__first_name: string;
assignees__last_name: string;
assignees__display_name: string;
count: number;
}
@ -76,9 +76,9 @@ export interface IDefaultAnalyticsResponse {
most_issue_closed_user: IDefaultAnalyticsUser[];
most_issue_created_user: {
created_by__avatar: string | null;
created_by__email: string | null;
created_by__first_name: string;
created_by__last_name: string;
created_by__display_name: string;
count: number;
}[];
open_estimate_sum: number;

View File

@ -49,6 +49,7 @@ export type TAssigneesDistribution = {
completed_issues: number;
first_name: string | null;
last_name: string | null;
display_name: string | null;
pending_issues: number;
total_issues: number;
};

View File

@ -3,6 +3,7 @@ import type {
IUserLite,
IWorkspace,
IWorkspaceLite,
IUserMemberLite,
TIssueGroupByOptions,
TIssueOrderByOptions,
TIssueViewOptions,
@ -78,7 +79,7 @@ type ProjectPreferences = {
export interface IProjectMember {
id: string;
member: IUserLite;
member: IUserMemberLite;
project: IProjectLite;
workspace: IWorkspaceLite;
comment: string;

View File

@ -15,6 +15,7 @@ export interface IUser {
created_location: readonly string;
date_joined: readonly Date;
email: string;
display_name: string;
first_name: string;
id: readonly string;
is_email_verified: boolean;
@ -53,13 +54,17 @@ export interface ICurrentUserResponse extends IUser {
export interface IUserLite {
avatar: string;
created_at: Date;
email: string;
display_name: string;
first_name: string;
readonly id: string;
is_bot: boolean;
last_name: string;
}
export interface IUserMemberLite extends IUserLite {
email: string;
}
export interface IUserActivity {
created_date: string;
activity_count: number;
@ -141,7 +146,7 @@ export interface IUserProfileProjectSegregation {
avatar: string;
cover_image: string | null;
date_joined: Date;
email: string;
display_name: string;
first_name: string;
last_name: string;
user_timezone: string;

View File

@ -2,7 +2,7 @@ import type {
IIssueFilterOptions,
IProjectMember,
IUser,
IUserLite,
IUserMemberLite,
TIssueGroupByOptions,
TIssueOrderByOptions,
TIssueViewOptions,
@ -73,9 +73,8 @@ export interface IWorkspaceViewProps {
export interface IWorkspaceMember {
readonly id: string;
user: IUserLite;
workspace: IWorkspace;
member: IUserLite;
member: IUserMemberLite;
role: 5 | 10 | 15 | 20;
company_role: string | null;
view_props: IWorkspaceViewProps;