|
|
@ -14,6 +14,9 @@ import { CheckEmoji } from '../ReactIcons/CheckEmoji.tsx'; |
|
|
|
import { ConstructionEmoji } from '../ReactIcons/ConstructionEmoji.tsx'; |
|
|
|
import { ConstructionEmoji } from '../ReactIcons/ConstructionEmoji.tsx'; |
|
|
|
import { BookEmoji } from '../ReactIcons/BookEmoji.tsx'; |
|
|
|
import { BookEmoji } from '../ReactIcons/BookEmoji.tsx'; |
|
|
|
import { DashboardAiRoadmaps } from './DashboardAiRoadmaps.tsx'; |
|
|
|
import { DashboardAiRoadmaps } from './DashboardAiRoadmaps.tsx'; |
|
|
|
|
|
|
|
import type { AllowedProfileVisibility } from '../../api/user.ts'; |
|
|
|
|
|
|
|
import { PencilIcon, type LucideIcon } from 'lucide-react'; |
|
|
|
|
|
|
|
import { cn } from '../../lib/classname.ts'; |
|
|
|
|
|
|
|
|
|
|
|
type UserDashboardResponse = { |
|
|
|
type UserDashboardResponse = { |
|
|
|
name: string; |
|
|
|
name: string; |
|
|
@ -21,6 +24,7 @@ type UserDashboardResponse = { |
|
|
|
avatar: string; |
|
|
|
avatar: string; |
|
|
|
headline: string; |
|
|
|
headline: string; |
|
|
|
username: string; |
|
|
|
username: string; |
|
|
|
|
|
|
|
profileVisibility: AllowedProfileVisibility; |
|
|
|
progresses: UserProgress[]; |
|
|
|
progresses: UserProgress[]; |
|
|
|
projects: ProjectStatusDocument[]; |
|
|
|
projects: ProjectStatusDocument[]; |
|
|
|
aiRoadmaps: { |
|
|
|
aiRoadmaps: { |
|
|
@ -222,18 +226,20 @@ export function PersonalDashboard(props: PersonalDashboardProps) { |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { username } = personalDashboardDetails || {}; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<section> |
|
|
|
<section> |
|
|
|
{isLoading ? ( |
|
|
|
{isLoading ? ( |
|
|
|
<div className="h-7 w-1/4 animate-pulse rounded-lg bg-gray-200"></div> |
|
|
|
<div className="h-7 w-1/4 animate-pulse rounded-lg bg-gray-200"></div> |
|
|
|
) : ( |
|
|
|
) : ( |
|
|
|
<div className="flex items-start sm:items-center justify-between flex-col sm:flex-row gap-1"> |
|
|
|
<div className="flex flex-col items-start justify-between gap-1 sm:flex-row sm:items-center"> |
|
|
|
<h2 className="text-lg font-medium"> |
|
|
|
<h2 className="text-lg font-medium"> |
|
|
|
Hi {name}, good {getCurrentPeriod()}! |
|
|
|
Hi {name}, good {getCurrentPeriod()}! |
|
|
|
</h2> |
|
|
|
</h2> |
|
|
|
<a |
|
|
|
<a |
|
|
|
href="/home" |
|
|
|
href="/home" |
|
|
|
className="text-xs font-medium bg-gray-200 hover:bg-gray-300 px-2.5 py-1 rounded-full text-gray-700 hover:text-black" |
|
|
|
className="rounded-full bg-gray-200 px-2.5 py-1 text-xs font-medium text-gray-700 hover:bg-gray-300 hover:text-black" |
|
|
|
> |
|
|
|
> |
|
|
|
Visit Homepage |
|
|
|
Visit Homepage |
|
|
|
</a> |
|
|
|
</a> |
|
|
@ -253,8 +259,16 @@ export function PersonalDashboard(props: PersonalDashboardProps) { |
|
|
|
<DashboardCard |
|
|
|
<DashboardCard |
|
|
|
imgUrl={avatarLink} |
|
|
|
imgUrl={avatarLink} |
|
|
|
title={name!} |
|
|
|
title={name!} |
|
|
|
description="Setup your profile" |
|
|
|
description={ |
|
|
|
href="/account/update-profile" |
|
|
|
username ? 'View your profile' : 'Setup your profile' |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
href={username ? `/u/${username}` : '/account/update-profile'} |
|
|
|
|
|
|
|
{...(username && { |
|
|
|
|
|
|
|
externalLinkIcon: PencilIcon, |
|
|
|
|
|
|
|
externalLinkHref: '/account/update-profile', |
|
|
|
|
|
|
|
externalLinkText: 'Edit', |
|
|
|
|
|
|
|
})} |
|
|
|
|
|
|
|
className={username ? 'border-dashed' : ''} |
|
|
|
/> |
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
<DashboardCard |
|
|
|
<DashboardCard |
|
|
@ -312,33 +326,61 @@ type DashboardCardProps = { |
|
|
|
title: string; |
|
|
|
title: string; |
|
|
|
description: string; |
|
|
|
description: string; |
|
|
|
href: string; |
|
|
|
href: string; |
|
|
|
|
|
|
|
externalLinkIcon?: LucideIcon; |
|
|
|
|
|
|
|
externalLinkText?: string; |
|
|
|
|
|
|
|
externalLinkHref?: string; |
|
|
|
|
|
|
|
className?: string; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
function DashboardCard(props: DashboardCardProps) { |
|
|
|
function DashboardCard(props: DashboardCardProps) { |
|
|
|
const { icon: Icon, imgUrl, title, description, href } = props; |
|
|
|
const { |
|
|
|
|
|
|
|
icon: Icon, |
|
|
|
|
|
|
|
imgUrl, |
|
|
|
|
|
|
|
title, |
|
|
|
|
|
|
|
description, |
|
|
|
|
|
|
|
href, |
|
|
|
|
|
|
|
externalLinkHref, |
|
|
|
|
|
|
|
externalLinkIcon: ExternalLinkIcon, |
|
|
|
|
|
|
|
externalLinkText, |
|
|
|
|
|
|
|
className, |
|
|
|
|
|
|
|
} = props; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<a |
|
|
|
<div |
|
|
|
href={href} |
|
|
|
className={cn( |
|
|
|
className="flex flex-col overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50" |
|
|
|
'relative overflow-hidden rounded-lg border border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50', |
|
|
|
> |
|
|
|
className, |
|
|
|
{Icon && ( |
|
|
|
|
|
|
|
<div className="px-4 pb-3 pt-4"> |
|
|
|
|
|
|
|
<Icon className="size-6" /> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
)} |
|
|
|
)} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<a href={href} className="flex flex-col"> |
|
|
|
|
|
|
|
{Icon && ( |
|
|
|
|
|
|
|
<div className="px-4 pb-3 pt-4"> |
|
|
|
|
|
|
|
<Icon className="size-6" /> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{imgUrl && ( |
|
|
|
|
|
|
|
<div className="px-4 pb-1.5 pt-3.5"> |
|
|
|
|
|
|
|
<img src={imgUrl} alt={title} className="size-8 rounded-full" /> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
{imgUrl && ( |
|
|
|
<div className="flex grow flex-col justify-center gap-0.5 p-4"> |
|
|
|
<div className="px-4 pb-1.5 pt-3.5"> |
|
|
|
<h3 className="truncate font-medium text-black">{title}</h3> |
|
|
|
<img src={imgUrl} alt={title} className="size-8 rounded-full" /> |
|
|
|
<p className="text-xs text-black">{description}</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
</a> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{externalLinkHref && ( |
|
|
|
|
|
|
|
<a |
|
|
|
|
|
|
|
href={externalLinkHref} |
|
|
|
|
|
|
|
className="absolute right-0 top-0 flex items-center gap-1.5 rounded-bl-md bg-gray-200 p-1 px-2 text-sm text-gray-600 hover:bg-gray-300 hover:text-black" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{ExternalLinkIcon && <ExternalLinkIcon className="size-3" />} |
|
|
|
|
|
|
|
{externalLinkText} |
|
|
|
|
|
|
|
</a> |
|
|
|
)} |
|
|
|
)} |
|
|
|
|
|
|
|
</div> |
|
|
|
<div className="flex grow flex-col justify-center gap-0.5 p-4"> |
|
|
|
|
|
|
|
<h3 className="truncate font-medium text-black">{title}</h3> |
|
|
|
|
|
|
|
<p className="text-xs text-black">{description}</p> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</a> |
|
|
|
|
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|