parent
a143b0ec20
commit
6abc5ff916
6 changed files with 200 additions and 183 deletions
@ -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…
Reference in new issue