Empty screen handling

pull/8189/head
Kamran Ahmed 3 months ago
parent d7f74b34a6
commit 94231874b0
  1. 5
      src/components/Dashboard/PersonalDashboard.tsx
  2. 69
      src/components/HeroSection/FavoriteRoadmaps.tsx
  3. 16
      src/components/HeroSection/HeroItemsGroup.tsx
  4. 8
      src/components/HeroSection/HeroTitle.tsx

@ -299,7 +299,10 @@ export function PersonalDashboard(props: PersonalDashboardProps) {
<div className="relative mt-6 border-t border-t-[#1e293c] pt-12">
<div className="container">
<h2 className="text-md font-regular absolute -top-[17px] flex rounded-lg border border-[#1e293c] bg-slate-900 px-3 py-1 text-slate-400 sm:left-1/2 sm:-translate-x-1/2">
<h2
id="role-based-roadmaps"
className="text-md font-regular absolute -top-[17px] flex rounded-lg border border-[#1e293c] bg-slate-900 px-3 py-1 text-slate-400 sm:left-1/2 sm:-translate-x-1/2"
>
Role Based Roadmaps
</h2>

@ -5,6 +5,8 @@ import {
Sparkle,
Eye,
EyeOff,
Square,
SquareCheckBig,
} from 'lucide-react';
import { useState } from 'react';
import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions.tsx';
@ -14,6 +16,7 @@ import { HeroProject } from './HeroProject';
import { HeroRoadmap } from './HeroRoadmap';
import { CreateRoadmapButton } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx';
import { HeroItemsGroup } from './HeroItemsGroup';
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx';
export type AIRoadmapType = {
id: string;
@ -34,6 +37,7 @@ type FavoriteRoadmapsProps = {
export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
const { progress, isLoading, customRoadmaps, aiRoadmaps, projects } = props;
const [showCompleted, setShowCompleted] = useState(false);
const [isCreatingCustomRoadmap, setIsCreatingCustomRoadmap] = useState(false);
const completedProjects = projects.filter(
(project) => project.submittedAt && project.repositoryUrl,
@ -49,10 +53,31 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
return (
<div className="flex flex-col">
{isCreatingCustomRoadmap && (
<CreateRoadmapModal
onClose={() => {
setIsCreatingCustomRoadmap(false);
}}
/>
)}
<HeroItemsGroup
icon={<CheckIcon additionalClasses="mr-1.5 h-[14px] w-[14px]" />}
isLoading={isLoading}
title="Your progress and bookmarks"
isEmpty={progress.length === 0}
emptyTitle={
<>
No bookmarked roadmaps yet
<a
href="#role-based-roadmaps"
className="ml-1.5 inline-flex items-center gap-1 font-medium text-blue-500 underline-offset-2 hover:underline"
>
<SquareCheckBig className="size-3.5" strokeWidth={2.5} />
Bookmark a roadmap
</a>
</>
}
>
{progress.map((resource) => (
<HeroRoadmap
@ -71,13 +96,27 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
}
/>
))}
<CreateRoadmapButton />
</HeroItemsGroup>
<HeroItemsGroup
icon={<MapIcon className="mr-1.5 h-[14px] w-[14px]" />}
isLoading={isLoading}
title="Your custom roadmaps"
isEmpty={customRoadmaps.length === 0}
emptyTitle={
<>
No custom roadmaps found
<button
onClick={() => {
setIsCreatingCustomRoadmap(true);
}}
className="ml-1.5 inline-flex items-center gap-1 font-medium text-blue-500 underline-offset-2 hover:underline"
>
<SquareCheckBig className="size-3.5" strokeWidth={2.5} />
Create custom roadmap
</button>
</>
}
>
{customRoadmaps.map((customRoadmap) => (
<HeroRoadmap
@ -101,6 +140,21 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
icon={<Sparkle className="mr-1.5 h-[14px] w-[14px]" />}
isLoading={isLoading}
title="Your AI roadmaps"
isEmpty={aiRoadmaps.length === 0}
emptyTitle={
<>
No AI roadmaps found
<button
onClick={() => {
setIsCreatingCustomRoadmap(true);
}}
className="ml-1.5 inline-flex items-center gap-1 font-medium text-blue-500 underline-offset-2 hover:underline"
>
<SquareCheckBig className="size-3.5" strokeWidth={2.5} />
Generate AI roadmap
</button>
</>
}
>
{aiRoadmaps.map((aiRoadmap) => (
<HeroRoadmap
@ -130,6 +184,19 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) {
icon={<FolderKanban className="mr-1.5 h-[14px] w-[14px]" />}
isLoading={isLoading}
title="Your active projects"
isEmpty={projectsToShow.length === 0}
emptyTitle={
<>
No active projects found
<a
href="/projects"
className="ml-1.5 inline-flex items-center gap-1 font-medium text-blue-500 underline-offset-2 hover:underline"
>
<SquareCheckBig className="size-3.5" strokeWidth={2.5} />
Start a new project
</a>
</>
}
rightContent={
completedProjects.length > 0 && (
<button

@ -5,6 +5,8 @@ import { HeroTitle } from './HeroTitle';
type HeroItemsGroupProps = {
icon: any;
isLoading?: boolean;
isEmpty?: boolean;
emptyTitle?: ReactNode;
title: string | ReactNode;
rightContent?: ReactNode;
children?: ReactNode;
@ -15,14 +17,14 @@ export function HeroItemsGroup(props: HeroItemsGroupProps) {
const {
icon,
isLoading = false,
isEmpty = false,
emptyTitle,
title,
rightContent,
children,
className,
} = props;
const isInitialRender = useRef(true);
const storageKey = `hero-group-${title}-collapsed`;
const [isCollapsed, setIsCollapsed] = useState(true);
@ -36,15 +38,15 @@ export function HeroItemsGroup(props: HeroItemsGroupProps) {
setIsCollapsed(isCollapsedByStorage());
}, [isLoading]);
const isLoadingOrCollapsed = isLoading || isCollapsed;
const isLoadingOrCollapsedOrEmpty = isLoading || isCollapsed || isEmpty;
return (
<div
className={cn(
'border-b border-gray-800/50',
{
'py-4': !isLoadingOrCollapsed,
'py-3': isLoadingOrCollapsed,
'py-4': !isLoadingOrCollapsedOrEmpty,
'py-3': isLoadingOrCollapsedOrEmpty,
'opacity-50 transition-opacity hover:opacity-100':
isCollapsed && !isLoading,
},
@ -55,6 +57,8 @@ export function HeroItemsGroup(props: HeroItemsGroupProps) {
<HeroTitle
icon={icon}
isLoading={isLoading}
isEmpty={isEmpty}
emptyTitle={emptyTitle}
title={title}
rightContent={rightContent}
isCollapsed={isCollapsed}
@ -63,7 +67,7 @@ export function HeroItemsGroup(props: HeroItemsGroupProps) {
localStorage.setItem(storageKey, (!isCollapsed).toString());
}}
/>
{!isLoadingOrCollapsed && (
{!isLoadingOrCollapsedOrEmpty && (
<div className="mt-4 grid grid-cols-1 gap-2.5 sm:grid-cols-2 md:grid-cols-3">
{children}
</div>

@ -10,6 +10,8 @@ type HeroTitleProps = {
rightContent?: ReactNode;
isCollapsed?: boolean;
onToggleCollapse?: () => void;
isEmpty?: boolean;
emptyTitle?: ReactNode;
};
export function HeroTitle(props: HeroTitleProps) {
@ -20,6 +22,8 @@ export function HeroTitle(props: HeroTitleProps) {
rightContent,
isCollapsed = false,
onToggleCollapse,
isEmpty = false,
emptyTitle,
} = props;
return (
@ -32,13 +36,13 @@ export function HeroTitle(props: HeroTitleProps) {
<Spinner />
</span>
)}
{title}
{!isEmpty ? title : emptyTitle || title}
</p>
</div>
<div className="flex items-center gap-2">
{!isCollapsed && rightContent}
{!isLoading && (
{!isLoading && !isEmpty && (
<button
onClick={onToggleCollapse}
className={cn(

Loading…
Cancel
Save