computer-scienceangular-roadmapbackend-roadmapblockchain-roadmapdba-roadmapdeveloper-roadmapdevops-roadmapfrontend-roadmapgo-roadmaphactoberfestjava-roadmapjavascript-roadmapnodejs-roadmappython-roadmapqa-roadmapreact-roadmaproadmapstudy-planvue-roadmapweb3-roadmap
223 lines
5.6 KiB
223 lines
5.6 KiB
import { useEffect, useState } from 'react'; |
|
import { httpGet } from '../../lib/http'; |
|
import { pageProgressMessage } from '../../stores/page'; |
|
import { MemberProgressItem } from './MemberProgressItem'; |
|
import { useToast } from '../../hooks/use-toast'; |
|
import { useStore } from '@nanostores/react'; |
|
import { $currentTeam } from '../../stores/team'; |
|
import { GroupRoadmapItem } from './GroupRoadmapItem'; |
|
import { getUrlParams, setUrlParams } from '../../lib/browser'; |
|
import { useAuth } from '../../hooks/use-auth'; |
|
import { MemberProgressModal } from './MemberProgressModal'; |
|
|
|
export type UserProgress = { |
|
resourceTitle: string; |
|
resourceType: 'roadmap' | 'best-practice'; |
|
resourceId: string; |
|
isFavorite: boolean; |
|
done: number; |
|
learning: number; |
|
skipped: number; |
|
total: number; |
|
updatedAt: string; |
|
}; |
|
|
|
export type TeamMember = { |
|
_id: string; |
|
role: string; |
|
name: string; |
|
email: string; |
|
avatar: string; |
|
progress: UserProgress[]; |
|
updatedAt: string; |
|
}; |
|
|
|
export type GroupByRoadmap = { |
|
resourceId: string; |
|
resourceTitle: string; |
|
resourceType: string; |
|
members: { |
|
member: TeamMember; |
|
progress: UserProgress | undefined; |
|
}[]; |
|
}; |
|
|
|
const groupingTypes = [ |
|
{ label: 'Members', value: 'member' }, |
|
{ label: 'Roadmaps', value: 'roadmap' }, |
|
] as const; |
|
|
|
export function TeamProgressPage() { |
|
const { t: teamId, gb: groupBy } = getUrlParams(); |
|
|
|
const [isLoading, setIsLoading] = useState(true); |
|
const toast = useToast(); |
|
const currentTeam = useStore($currentTeam); |
|
const user = useAuth(); |
|
|
|
const [showMemberProgress, setShowMemberProgress] = useState<{ |
|
resourceId: string; |
|
member: TeamMember; |
|
}>(); |
|
|
|
const [teamMembers, setTeamMembers] = useState<TeamMember[]>([]); |
|
const [selectedGrouping, setSelectedGrouping] = useState< |
|
'roadmap' | 'member' |
|
>(groupBy || 'member'); |
|
|
|
async function getTeamProgress() { |
|
const { response, error } = await httpGet<TeamMember[]>( |
|
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-progress/${teamId}` |
|
); |
|
if (error || !response) { |
|
toast.error(error?.message || 'Failed to get team progress'); |
|
return; |
|
} |
|
|
|
setTeamMembers( |
|
response.sort((a, b) => { |
|
if (a.email === user?.email) { |
|
return -1; |
|
} |
|
if (b.email === user?.email) { |
|
return 1; |
|
} |
|
return 0; |
|
}) |
|
); |
|
} |
|
|
|
useEffect(() => { |
|
if (!teamId) { |
|
return; |
|
} |
|
|
|
getTeamProgress().then(() => { |
|
pageProgressMessage.set(''); |
|
setIsLoading(false); |
|
}); |
|
}, [teamId]); |
|
|
|
useEffect(() => { |
|
if (!selectedGrouping) { |
|
return; |
|
} |
|
|
|
setUrlParams({ gb: selectedGrouping }); |
|
}, [selectedGrouping]); |
|
|
|
const groupByRoadmap: GroupByRoadmap[] = []; |
|
for (const roadmap of currentTeam?.roadmaps || []) { |
|
const members: GroupByRoadmap['members'] = []; |
|
for (const member of teamMembers) { |
|
const progress = member.progress.find( |
|
(progress) => progress.resourceId === roadmap |
|
); |
|
if (!progress) { |
|
continue; |
|
} |
|
members.push({ |
|
member, |
|
progress, |
|
}); |
|
} |
|
|
|
if (!members.length) { |
|
continue; |
|
} |
|
|
|
groupByRoadmap.push({ |
|
resourceId: roadmap, |
|
resourceTitle: members?.[0].progress?.resourceTitle || '', |
|
resourceType: 'roadmap', |
|
members, |
|
}); |
|
} |
|
|
|
if (!teamId) { |
|
window.location.href = '/'; |
|
return; |
|
} |
|
|
|
if (isLoading) { |
|
return null; |
|
} |
|
|
|
return ( |
|
<div> |
|
{showMemberProgress && ( |
|
<MemberProgressModal |
|
member={showMemberProgress.member} |
|
teamId={teamId} |
|
resourceId={showMemberProgress.resourceId} |
|
resourceType={'roadmap'} |
|
onClose={() => { |
|
setShowMemberProgress(undefined); |
|
}} |
|
onShowMyProgress={() => { |
|
setShowMemberProgress({ |
|
resourceId: showMemberProgress.resourceId, |
|
member: teamMembers.find( |
|
(member) => member.email === user?.email |
|
)!, |
|
}); |
|
}} |
|
/> |
|
)} |
|
|
|
<div className="flex items-center gap-2"> |
|
{groupingTypes.map((grouping) => ( |
|
<button |
|
key={grouping.value} |
|
className={`rounded-md border p-1 px-2 text-sm ${ |
|
selectedGrouping === grouping.value |
|
? ' border-gray-400 bg-gray-200 ' |
|
: '' |
|
}`} |
|
onClick={() => setSelectedGrouping(grouping.value)} |
|
> |
|
{grouping.label} |
|
</button> |
|
))} |
|
</div> |
|
|
|
<div className="mt-6"> |
|
{selectedGrouping === 'roadmap' && ( |
|
<div className="grid gap-4 sm:grid-cols-2"> |
|
{groupByRoadmap.map((roadmap) => { |
|
return ( |
|
<GroupRoadmapItem |
|
key={roadmap.resourceId} |
|
roadmap={roadmap} |
|
onShowResourceProgress={(member, resourceId) => { |
|
setShowMemberProgress({ |
|
resourceId, |
|
member, |
|
}); |
|
}} |
|
/> |
|
); |
|
})} |
|
</div> |
|
)} |
|
{selectedGrouping === 'member' && ( |
|
<div className="grid gap-4 sm:grid-cols-2"> |
|
{teamMembers.map((member) => ( |
|
<MemberProgressItem |
|
key={member._id} |
|
member={member} |
|
isMyProgress={member?.email === user?.email} |
|
onShowResourceProgress={(resourceId) => { |
|
setShowMemberProgress({ |
|
resourceId, |
|
member, |
|
}); |
|
}} |
|
/> |
|
))} |
|
</div> |
|
)} |
|
</div> |
|
</div> |
|
); |
|
}
|
|
|