Move to separate files

pull/8189/head
Kamran Ahmed 2 months ago
parent a143b0ec20
commit 6abc5ff916
  1. 2
      .astro/settings.json
  2. 4
      src/components/Dashboard/PersonalDashboard.tsx
  3. 223
      src/components/HeroSection/FavoriteRoadmaps.tsx
  4. 52
      src/components/HeroSection/HeroProject.tsx
  5. 74
      src/components/HeroSection/HeroRoadmap.tsx
  6. 28
      src/components/HeroSection/HeroTitle.tsx

@ -3,6 +3,6 @@
"enabled": false
},
"_variables": {
"lastUpdateCheck": 1738019390029
"lastUpdateCheck": 1739229597159
}
}

@ -17,11 +17,11 @@ import { $accountStreak, type StreakResponse } from '../../stores/streak';
import type { PageType } from '../CommandMenu/CommandMenu';
import {
FavoriteRoadmaps,
HeroRoadmap,
type AIRoadmapType,
} from '../HeroSection/FavoriteRoadmaps.tsx';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions';
import type { UserProgress } from '../TeamProgress/TeamProgressPage';
import { HeroRoadmap } from '../HeroSection/HeroRoadmap.tsx';
type UserDashboardResponse = {
name: string;
@ -106,7 +106,7 @@ function DashboardStats(props: DashboardStatsProps) {
} = props;
return (
<div className="container flex items-center justify-between gap-2 pb-2 pt-6 text-sm text-slate-400 mb-3">
<div className="container mb-3 flex items-center justify-between gap-2 pb-2 pt-6 text-sm text-slate-400">
<div className="flex items-center gap-2">
<DashboardStatItem
icon={Zap}

@ -3,25 +3,18 @@ import {
MapIcon,
Plus,
Sparkle,
ThumbsUp,
ChevronDown,
ChevronUp,
Eye,
EyeOff,
CircleDashed,
Circle,
} from 'lucide-react';
import type { ReactNode } from 'react';
import type { ResourceType } from '../../lib/resource-progress.ts';
import { CreateRoadmapButton } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx';
import { MarkFavorite } from '../FeaturedItems/MarkFavorite.tsx';
import { CheckIcon } from '../ReactIcons/CheckIcon.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import type { UserProgress } from '../TeamProgress/TeamProgressPage.tsx';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions.tsx';
import { getRelativeTimeString } from '../../lib/date';
import { useState } from 'react';
import { cn } from '../../lib/classname.ts';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions.tsx';
import { CheckIcon } from '../ReactIcons/CheckIcon.tsx';
import type { UserProgress } from '../TeamProgress/TeamProgressPage.tsx';
import { HeroProject } from './HeroProject';
import { HeroRoadmap } from './HeroRoadmap';
import { HeroTitle } from './HeroTitle';
import { CreateRoadmapButton } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx';
export type AIRoadmapType = {
id: string;
@ -29,103 +22,6 @@ export type AIRoadmapType = {
slug: string;
};
type ProgressRoadmapProps = {
url: string;
percentageDone: number;
allowFavorite?: boolean;
resourceId: string;
resourceType: ResourceType;
resourceTitle: string;
isFavorite?: boolean;
isTrackable?: boolean;
isNew?: boolean;
};
export function HeroRoadmap(props: ProgressRoadmapProps) {
const {
url,
percentageDone,
resourceType,
resourceId,
resourceTitle,
isFavorite,
allowFavorite = true,
isTrackable = true,
isNew = false,
} = props;
return (
<a
href={url}
className={cn(
'relative flex flex-col overflow-hidden rounded-md border p-3 text-sm text-slate-400 hover:text-slate-300',
{
'border-slate-800 bg-slate-900 hover:border-slate-600': isTrackable,
'border-slate-700/50 bg-slate-800/50 hover:border-slate-600/70':
!isTrackable,
},
)}
>
<span title={resourceTitle} className="relative z-20 truncate">
{resourceTitle}
</span>
{isTrackable && (
<span
className="absolute bottom-0 left-0 top-0 z-10 bg-[#172a3a]"
style={{ width: `${percentageDone}%` }}
></span>
)}
{allowFavorite && (
<MarkFavorite
resourceId={resourceId}
resourceType={resourceType}
favorite={isFavorite}
/>
)}
{isNew && (
<span className="absolute bottom-1.5 right-2 flex items-center rounded-br rounded-tl text-xs font-medium text-purple-300">
<span className="mr-1.5 flex h-2 w-2">
<span className="absolute inline-flex h-2 w-2 animate-ping rounded-full bg-purple-400 opacity-75" />
<span className="relative inline-flex h-2 w-2 rounded-full bg-purple-500" />
</span>
New
</span>
)}
</a>
);
}
type HeroTitleProps = {
icon: any;
isLoading?: boolean;
title: string | ReactNode;
rightContent?: ReactNode;
};
function HeroTitle(props: HeroTitleProps) {
const { isLoading = false, title, icon, rightContent } = props;
return (
<div className="flex items-center justify-between">
<p className="flex items-center text-sm text-gray-400">
{!isLoading && icon}
{isLoading && (
<span className="mr-1.5">
<Spinner />
</span>
)}
{title}
</p>
<div>{rightContent}</div>
</div>
);
}
type FavoriteRoadmapsProps = {
progress: UserProgress[];
projects: (ProjectStatusDocument & {
@ -137,56 +33,15 @@ type FavoriteRoadmapsProps = {
isAllCollapsed: boolean;
};
type HeroProjectProps = {
project: ProjectStatusDocument & {
title: string;
};
};
export function HeroProject({ project }: HeroProjectProps) {
return (
<a
href={`/projects/${project.projectId}`}
className="group relative flex flex-col justify-between gap-2 rounded-md border border-slate-800 bg-slate-900 p-3.5 hover:border-slate-600"
>
<div className="relative z-10 flex items-start justify-between gap-2">
<h3 className="truncate font-medium text-slate-300 group-hover:text-slate-100">
{project.title}
</h3>
<span
className={cn(
'absolute -right-2 -top-2 flex flex-shrink-0 items-center gap-1 rounded-full text-xs uppercase tracking-wide',
{
'text-green-600/50': project.submittedAt && project.repositoryUrl,
'text-yellow-600': !project.submittedAt || !project.repositoryUrl,
},
)}
>
{project.submittedAt && project.repositoryUrl ? 'Done' : ''}
</span>
</div>
<div className="relative z-10 flex items-center gap-2 text-xs text-slate-400">
{project.submittedAt && project.repositoryUrl && (
<span className="flex items-center gap-1">
<ThumbsUp className="h-3 w-3" />
{project.upvotes}
</span>
)}
{project.startedAt && (
<span>Started {getRelativeTimeString(project.startedAt)}</span>
)}
</div>
<div className="absolute inset-0 rounded-md bg-gradient-to-br from-slate-800/50 via-transparent to-transparent" />
{project.submittedAt && project.repositoryUrl && (
<div className="absolute inset-0 rounded-md bg-gradient-to-br from-green-950/20 via-transparent to-transparent" />
)}
</a>
);
}
export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
const { progress, isLoading, customRoadmaps, aiRoadmaps, projects, isAllCollapsed } = props;
const {
progress,
isLoading,
customRoadmaps,
aiRoadmaps,
projects,
isAllCollapsed,
} = props;
const [showCompleted, setShowCompleted] = useState(false);
const completedProjects = projects.filter(
@ -203,10 +58,12 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
return (
<div className="flex flex-col">
<div className={cn("", {
"border-b border-b-slate-800/70 pt-5 pb-5": !isAllCollapsed,
"py-2": isAllCollapsed
})}>
<div
className={cn('', {
'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed,
'py-2': isAllCollapsed,
})}
>
<div className="container">
<HeroTitle
icon={
@ -218,7 +75,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
title="Your progress and bookmarks"
/>
{!isLoading && progress.length > 0 && !isAllCollapsed && (
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3 mt-3">
<div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
{progress.map((resource) => (
<HeroRoadmap
key={`${resource.resourceType}-${resource.resourceId}`}
@ -242,10 +99,12 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
</div>
</div>
<div className={cn("", {
"border-b border-b-slate-800/70 pt-5 pb-5": !isAllCollapsed,
"py-2": isAllCollapsed
})}>
<div
className={cn('', {
'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed,
'py-2': isAllCollapsed,
})}
>
<div className="container">
<HeroTitle
icon={(<MapIcon className="mr-1.5 h-[14px] w-[14px]" />) as any}
@ -253,7 +112,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
title="Your custom roadmaps"
/>
{!isLoading && customRoadmaps.length > 0 && !isAllCollapsed && (
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3 mt-3">
<div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
{customRoadmaps.map((customRoadmap) => (
<HeroRoadmap
key={customRoadmap.resourceId}
@ -275,10 +134,12 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
</div>
</div>
<div className={cn("", {
"border-b border-b-slate-800/70 pt-5 pb-5": !isAllCollapsed,
"py-2": isAllCollapsed
})}>
<div
className={cn('', {
'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed,
'py-2': isAllCollapsed,
})}
>
<div className="container">
<HeroTitle
icon={(<Sparkle className="mr-1.5 h-[14px] w-[14px]" />) as any}
@ -286,7 +147,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
title="Your AI roadmaps"
/>
{!isLoading && aiRoadmaps.length > 0 && !isAllCollapsed && (
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3 mt-3">
<div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
{aiRoadmaps.map((aiRoadmap) => (
<HeroRoadmap
key={aiRoadmap.id}
@ -314,10 +175,12 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
</div>
</div>
<div className={cn("", {
"border-b border-b-slate-800/70 pt-5 pb-5": !isAllCollapsed,
"py-2": isAllCollapsed
})}>
<div
className={cn('', {
'border-b border-b-slate-800/70 pb-5 pt-5': !isAllCollapsed,
'py-2': isAllCollapsed,
})}
>
<div className="container">
<HeroTitle
icon={
@ -342,7 +205,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
}
/>
{!isLoading && projectsToShow.length > 0 && !isAllCollapsed && (
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3 mt-3">
<div className="mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
{projectsToShow.map((project) => (
<HeroProject key={project._id} project={project} />
))}

@ -0,0 +1,52 @@
import { ThumbsUp } from 'lucide-react';
import { cn } from '../../lib/classname.ts';
import { getRelativeTimeString } from '../../lib/date';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions.tsx';
type HeroProjectProps = {
project: ProjectStatusDocument & {
title: string;
};
};
export function HeroProject({ project }: HeroProjectProps) {
return (
<a
href={`/projects/${project.projectId}`}
className="group relative flex flex-col justify-between gap-2 rounded-md border border-slate-800 bg-slate-900 p-3.5 hover:border-slate-600"
>
<div className="relative z-10 flex items-start justify-between gap-2">
<h3 className="truncate font-medium text-slate-300 group-hover:text-slate-100">
{project.title}
</h3>
<span
className={cn(
'absolute -right-2 -top-2 flex flex-shrink-0 items-center gap-1 rounded-full text-xs uppercase tracking-wide',
{
'text-green-600/50': project.submittedAt && project.repositoryUrl,
'text-yellow-600': !project.submittedAt || !project.repositoryUrl,
},
)}
>
{project.submittedAt && project.repositoryUrl ? 'Done' : ''}
</span>
</div>
<div className="relative z-10 flex items-center gap-2 text-xs text-slate-400">
{project.submittedAt && project.repositoryUrl && (
<span className="flex items-center gap-1">
<ThumbsUp className="h-3 w-3" />
{project.upvotes}
</span>
)}
{project.startedAt && (
<span>Started {getRelativeTimeString(project.startedAt)}</span>
)}
</div>
<div className="absolute inset-0 rounded-md bg-gradient-to-br from-slate-800/50 via-transparent to-transparent" />
{project.submittedAt && project.repositoryUrl && (
<div className="absolute inset-0 rounded-md bg-gradient-to-br from-green-950/20 via-transparent to-transparent" />
)}
</a>
);
}

@ -0,0 +1,74 @@
import { cn } from '../../lib/classname.ts';
import type { ResourceType } from '../../lib/resource-progress.ts';
import { MarkFavorite } from '../FeaturedItems/MarkFavorite.tsx';
type ProgressRoadmapProps = {
url: string;
percentageDone: number;
allowFavorite?: boolean;
resourceId: string;
resourceType: ResourceType;
resourceTitle: string;
isFavorite?: boolean;
isTrackable?: boolean;
isNew?: boolean;
};
export function HeroRoadmap(props: ProgressRoadmapProps) {
const {
url,
percentageDone,
resourceType,
resourceId,
resourceTitle,
isFavorite,
allowFavorite = true,
isTrackable = true,
isNew = false,
} = props;
return (
<a
href={url}
className={cn(
'relative flex flex-col overflow-hidden rounded-md border p-3 text-sm text-slate-400 hover:text-slate-300',
{
'border-slate-800 bg-slate-900 hover:border-slate-600': isTrackable,
'border-slate-700/50 bg-slate-800/50 hover:border-slate-600/70':
!isTrackable,
},
)}
>
<span title={resourceTitle} className="relative z-20 truncate">
{resourceTitle}
</span>
{isTrackable && (
<span
className="absolute bottom-0 left-0 top-0 z-10 bg-[#172a3a]"
style={{ width: `${percentageDone}%` }}
></span>
)}
{allowFavorite && (
<MarkFavorite
resourceId={resourceId}
resourceType={resourceType}
favorite={isFavorite}
/>
)}
{isNew && (
<span className="absolute bottom-1.5 right-2 flex items-center rounded-br rounded-tl text-xs font-medium text-purple-300">
<span className="mr-1.5 flex h-2 w-2">
<span className="absolute inline-flex h-2 w-2 animate-ping rounded-full bg-purple-400 opacity-75" />
<span className="relative inline-flex h-2 w-2 rounded-full bg-purple-500" />
</span>
New
</span>
)}
</a>
);
}

@ -0,0 +1,28 @@
import type { ReactNode } from 'react';
import { Spinner } from '../ReactIcons/Spinner.tsx';
type HeroTitleProps = {
icon: any;
isLoading?: boolean;
title: string | ReactNode;
rightContent?: ReactNode;
};
export function HeroTitle(props: HeroTitleProps) {
const { isLoading = false, title, icon, rightContent } = props;
return (
<div className="flex items-center justify-between">
<p className="flex items-center text-sm text-gray-400">
{!isLoading && icon}
{isLoading && (
<span className="mr-1.5">
<Spinner />
</span>
)}
{title}
</p>
<div>{rightContent}</div>
</div>
);
}
Loading…
Cancel
Save