computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
5.7 KiB
177 lines
5.7 KiB
9 months ago
|
import { useEffect, useState } from 'react';
|
||
|
import { httpGet } from '../../lib/http';
|
||
|
import { pageProgressMessage } from '../../stores/page';
|
||
|
import { getUrlParams } from '../../lib/browser';
|
||
|
import { useToast } from '../../hooks/use-toast';
|
||
|
import type { TeamMemberDocument } from '../TeamMembers/TeamMembersPage';
|
||
|
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
|
||
|
import type { TeamActivityStreamDocument } from '../TeamActivity/TeamActivityPage';
|
||
|
import { ResourceProgress } from '../Activity/ResourceProgress';
|
||
|
import { ActivityStream } from '../Activity/ActivityStream';
|
||
|
import { MemberRoleBadge } from '../TeamMembers/RoleBadge';
|
||
|
import { TeamMemberEmptyPage } from './TeamMemberEmptyPage';
|
||
|
import { Pagination } from '../Pagination/Pagination';
|
||
|
|
||
|
type GetTeamMemberProgressesResponse = TeamMemberDocument & {
|
||
|
name: string;
|
||
|
avatar: string;
|
||
|
email: string;
|
||
|
progresses: UserProgress[];
|
||
|
};
|
||
|
|
||
|
type GetTeamMemberActivityResponse = {
|
||
|
data: TeamActivityStreamDocument[];
|
||
|
totalCount: number;
|
||
|
totalPages: number;
|
||
|
currPage: number;
|
||
|
perPage: number;
|
||
|
};
|
||
|
|
||
|
export function TeamMemberDetailsPage() {
|
||
|
const { t: teamId, m: memberId } = getUrlParams() as { t: string; m: string };
|
||
|
|
||
|
const toast = useToast();
|
||
|
|
||
|
const [memberProgress, setMemberProgress] =
|
||
|
useState<GetTeamMemberProgressesResponse | null>(null);
|
||
|
const [memberActivity, setMemberActivity] =
|
||
|
useState<GetTeamMemberActivityResponse | null>(null);
|
||
|
const [currPage, setCurrPage] = useState(1);
|
||
|
|
||
|
const loadMemberProgress = async () => {
|
||
|
const { response, error } = await httpGet<GetTeamMemberProgressesResponse>(
|
||
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-member-progresses/${teamId}/${memberId}`,
|
||
|
);
|
||
|
if (error || !response) {
|
||
|
pageProgressMessage.set('');
|
||
|
toast.error(error?.message || 'Failed to load team member');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
setMemberProgress(response);
|
||
|
};
|
||
|
|
||
|
const loadMemberActivity = async (currPage: number = 1) => {
|
||
|
const { response, error } = await httpGet<GetTeamMemberActivityResponse>(
|
||
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-member-activity/${teamId}/${memberId}`,
|
||
|
{
|
||
|
currPage,
|
||
|
},
|
||
|
);
|
||
|
if (error || !response) {
|
||
|
pageProgressMessage.set('');
|
||
|
toast.error(error?.message || 'Failed to load team member activity');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
setMemberActivity(response);
|
||
|
setCurrPage(response?.currPage || 1);
|
||
|
};
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (!teamId) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Promise.allSettled([loadMemberProgress(), loadMemberActivity()]).finally(
|
||
|
() => {
|
||
|
pageProgressMessage.set('');
|
||
|
},
|
||
|
);
|
||
|
}, [teamId]);
|
||
|
|
||
|
if (!teamId || !memberId || !memberProgress || !memberActivity) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const avatarUrl = memberProgress?.avatar
|
||
|
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${memberProgress?.avatar}`
|
||
|
: '/images/default-avatar.png';
|
||
|
|
||
|
return (
|
||
|
<>
|
||
|
<div>
|
||
|
<div className="flex items-center gap-4">
|
||
|
<img
|
||
|
src={avatarUrl}
|
||
|
alt={memberProgress?.name}
|
||
|
className="h-24 w-24 rounded-full"
|
||
|
/>
|
||
|
<div>
|
||
|
<MemberRoleBadge
|
||
|
className="sm:inline-flex"
|
||
|
role={memberProgress?.role!}
|
||
|
/>
|
||
|
<h1 className="mt-1 text-2xl font-medium">
|
||
|
{memberProgress?.name}
|
||
|
</h1>
|
||
|
<p className="text-sm text-gray-500">{memberProgress?.email}</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<hr className="my-8 border-gray-200" />
|
||
|
|
||
|
{memberProgress?.progresses && memberProgress?.progresses?.length > 0 ? (
|
||
|
<>
|
||
|
<h2 className="mb-3 text-xs uppercase text-gray-400">
|
||
|
Progress Overview
|
||
|
</h2>
|
||
|
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
|
||
|
{memberProgress?.progresses?.map((progress) => {
|
||
|
const learningCount = progress.learning || 0;
|
||
|
const doneCount = progress.done || 0;
|
||
|
const totalCount = progress.total || 0;
|
||
|
const skippedCount = progress.skipped || 0;
|
||
|
|
||
|
return (
|
||
|
<ResourceProgress
|
||
|
key={progress.resourceId}
|
||
|
isCustomResource={progress.isCustomResource!}
|
||
|
doneCount={doneCount > totalCount ? totalCount : doneCount}
|
||
|
learningCount={
|
||
|
learningCount > totalCount ? totalCount : learningCount
|
||
|
}
|
||
|
totalCount={totalCount}
|
||
|
skippedCount={skippedCount}
|
||
|
resourceId={progress.resourceId}
|
||
|
resourceType={'roadmap'}
|
||
|
updatedAt={progress.updatedAt}
|
||
|
title={progress.resourceTitle}
|
||
|
roadmapSlug={progress.roadmapSlug}
|
||
|
showActions={false}
|
||
|
/>
|
||
|
);
|
||
|
})}
|
||
|
</div>
|
||
|
</>
|
||
|
) : (
|
||
|
<TeamMemberEmptyPage teamId={teamId} />
|
||
|
)}
|
||
|
|
||
|
{memberActivity?.data && memberActivity?.data?.length > 0 ? (
|
||
|
<>
|
||
|
<ActivityStream
|
||
|
className="mt-8 p-0 md:m-0 md:mb-4 md:mt-8 md:p-0"
|
||
|
activities={
|
||
|
memberActivity?.data?.flatMap((act) => act.activity) || []
|
||
|
}
|
||
|
/>
|
||
|
<Pagination
|
||
|
currPage={currPage}
|
||
|
totalPages={memberActivity?.totalPages || 1}
|
||
|
totalCount={memberActivity?.totalCount || 0}
|
||
|
perPage={memberActivity?.perPage || 10}
|
||
|
onPageChange={(page) => {
|
||
|
pageProgressMessage.set('Loading Activity');
|
||
|
loadMemberActivity(page).finally(() => {
|
||
|
pageProgressMessage.set('');
|
||
|
});
|
||
|
}}
|
||
|
/>
|
||
|
</>
|
||
|
) : null}
|
||
|
</>
|
||
|
);
|
||
|
}
|