|
|
@ -3,10 +3,10 @@ import { CheckIcon } from '../ReactIcons/CheckIcon'; |
|
|
|
import { MarkFavorite } from '../FeaturedItems/MarkFavorite'; |
|
|
|
import { MarkFavorite } from '../FeaturedItems/MarkFavorite'; |
|
|
|
import { Spinner } from '../ReactIcons/Spinner'; |
|
|
|
import { Spinner } from '../ReactIcons/Spinner'; |
|
|
|
import type { ResourceType } from '../../lib/resource-progress'; |
|
|
|
import type { ResourceType } from '../../lib/resource-progress'; |
|
|
|
import { MapIcon } from 'lucide-react'; |
|
|
|
import { MapIcon, Users2 } from 'lucide-react'; |
|
|
|
import { CreateRoadmapButton } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapButton'; |
|
|
|
import { CreateRoadmapButton } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapButton'; |
|
|
|
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal'; |
|
|
|
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal'; |
|
|
|
import { useState } from 'react'; |
|
|
|
import { type ReactNode, useState } from 'react'; |
|
|
|
import { TeamAnnouncement } from '../TeamAnnouncement'; |
|
|
|
import { TeamAnnouncement } from '../TeamAnnouncement'; |
|
|
|
|
|
|
|
|
|
|
|
type ProgressRoadmapProps = { |
|
|
|
type ProgressRoadmapProps = { |
|
|
@ -56,7 +56,7 @@ function HeroRoadmap(props: ProgressRoadmapProps) { |
|
|
|
type ProgressTitleProps = { |
|
|
|
type ProgressTitleProps = { |
|
|
|
icon: any; |
|
|
|
icon: any; |
|
|
|
isLoading?: boolean; |
|
|
|
isLoading?: boolean; |
|
|
|
title: string; |
|
|
|
title: string | ReactNode; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export function HeroTitle(props: ProgressTitleProps) { |
|
|
|
export function HeroTitle(props: ProgressTitleProps) { |
|
|
@ -74,25 +74,39 @@ export function HeroTitle(props: ProgressTitleProps) { |
|
|
|
</p> |
|
|
|
</p> |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
export type HeroTeamRoadmaps = Record<string, UserProgressResponse>; |
|
|
|
|
|
|
|
|
|
|
|
type ProgressListProps = { |
|
|
|
type ProgressListProps = { |
|
|
|
progress: UserProgressResponse; |
|
|
|
progress: UserProgressResponse; |
|
|
|
customRoadmaps: UserProgressResponse; |
|
|
|
customRoadmaps: UserProgressResponse; |
|
|
|
|
|
|
|
teamRoadmaps?: HeroTeamRoadmaps; |
|
|
|
isLoading?: boolean; |
|
|
|
isLoading?: boolean; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export function HeroRoadmaps(props: ProgressListProps) { |
|
|
|
export function HeroRoadmaps(props: ProgressListProps) { |
|
|
|
const { progress, isLoading = false, customRoadmaps } = props; |
|
|
|
const { |
|
|
|
|
|
|
|
teamRoadmaps = {}, |
|
|
|
|
|
|
|
progress, |
|
|
|
|
|
|
|
isLoading = false, |
|
|
|
|
|
|
|
customRoadmaps, |
|
|
|
|
|
|
|
} = props; |
|
|
|
|
|
|
|
|
|
|
|
const [isCreatingRoadmap, setIsCreatingRoadmap] = useState(false); |
|
|
|
const [isCreatingRoadmap, setIsCreatingRoadmap] = useState(false); |
|
|
|
|
|
|
|
const [creatingRoadmapTeamId, setCreatingRoadmapTeamId] = useState<string>(); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div className="relative pb-12 pt-4 sm:pt-7"> |
|
|
|
<div className="relative pb-12 pt-4 sm:pt-7"> |
|
|
|
<p className="mb-7 text-sm mt-2"> |
|
|
|
<p className="mb-7 mt-2 text-sm"> |
|
|
|
<TeamAnnouncement /> |
|
|
|
<TeamAnnouncement /> |
|
|
|
</p> |
|
|
|
</p> |
|
|
|
{isCreatingRoadmap && ( |
|
|
|
{isCreatingRoadmap && ( |
|
|
|
<CreateRoadmapModal onClose={() => setIsCreatingRoadmap(false)} /> |
|
|
|
<CreateRoadmapModal |
|
|
|
|
|
|
|
teamId={creatingRoadmapTeamId} |
|
|
|
|
|
|
|
onClose={() => { |
|
|
|
|
|
|
|
setIsCreatingRoadmap(false); |
|
|
|
|
|
|
|
setCreatingRoadmapTeamId(undefined); |
|
|
|
|
|
|
|
}} |
|
|
|
|
|
|
|
/> |
|
|
|
)} |
|
|
|
)} |
|
|
|
{ |
|
|
|
{ |
|
|
|
<HeroTitle |
|
|
|
<HeroTitle |
|
|
@ -168,6 +182,83 @@ export function HeroRoadmaps(props: ProgressListProps) { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
)} |
|
|
|
)} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{Object.keys(teamRoadmaps).map((teamName) => { |
|
|
|
|
|
|
|
const currentTeam: UserProgressResponse[0]['team'] = |
|
|
|
|
|
|
|
teamRoadmaps?.[teamName]?.[0]?.team; |
|
|
|
|
|
|
|
const roadmapsList = teamRoadmaps[teamName].filter( |
|
|
|
|
|
|
|
(roadmap) => !!roadmap.resourceTitle |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
const canManageTeam = ['admin', 'manager'].includes(currentTeam?.role!); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<div className="mt-5"> |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
<HeroTitle |
|
|
|
|
|
|
|
icon={<Users2 className="mr-1.5 h-[14px] w-[14px]" />} |
|
|
|
|
|
|
|
title={ |
|
|
|
|
|
|
|
<> |
|
|
|
|
|
|
|
Team{' '} |
|
|
|
|
|
|
|
<a |
|
|
|
|
|
|
|
className="mx-1 font-medium underline underline-offset-2 transition-colors hover:text-gray-300" |
|
|
|
|
|
|
|
href={`/team/progress?t=${currentTeam?.id}`} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{teamName} |
|
|
|
|
|
|
|
</a> |
|
|
|
|
|
|
|
Roadmaps |
|
|
|
|
|
|
|
</> |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{roadmapsList.length === 0 && ( |
|
|
|
|
|
|
|
<p className="rounded-md border border-dashed border-gray-800 p-2 text-sm text-gray-600"> |
|
|
|
|
|
|
|
Team does not have any roadmaps yet.{' '} |
|
|
|
|
|
|
|
{canManageTeam && ( |
|
|
|
|
|
|
|
<button |
|
|
|
|
|
|
|
className="text-gray-500 underline underline-offset-2 hover:text-gray-400" |
|
|
|
|
|
|
|
onClick={() => { |
|
|
|
|
|
|
|
setCreatingRoadmapTeamId(currentTeam?.id); |
|
|
|
|
|
|
|
setIsCreatingRoadmap(true); |
|
|
|
|
|
|
|
}} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
Create one! |
|
|
|
|
|
|
|
</button> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</p> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{roadmapsList.length > 0 && ( |
|
|
|
|
|
|
|
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3"> |
|
|
|
|
|
|
|
{roadmapsList.map((customRoadmap) => { |
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<HeroRoadmap |
|
|
|
|
|
|
|
key={customRoadmap.resourceId} |
|
|
|
|
|
|
|
resourceId={customRoadmap.resourceId} |
|
|
|
|
|
|
|
resourceType={'roadmap'} |
|
|
|
|
|
|
|
resourceTitle={customRoadmap.resourceTitle} |
|
|
|
|
|
|
|
percentageDone={ |
|
|
|
|
|
|
|
((customRoadmap.skipped + customRoadmap.done) / |
|
|
|
|
|
|
|
customRoadmap.total) * |
|
|
|
|
|
|
|
100 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
url={`/r?id=${customRoadmap.resourceId}`} |
|
|
|
|
|
|
|
allowFavorite={false} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
})} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{canManageTeam && ( |
|
|
|
|
|
|
|
<CreateRoadmapButton |
|
|
|
|
|
|
|
teamId={currentTeam?.id} |
|
|
|
|
|
|
|
text="Create Team Roadmap" |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
})} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|