|
|
@ -10,6 +10,7 @@ import { LoadingRoadmaps } from '../ExploreAIRoadmap/LoadingRoadmaps'; |
|
|
|
import { httpGet } from '../../lib/http'; |
|
|
|
import { httpGet } from '../../lib/http'; |
|
|
|
import { useToast } from '../../hooks/use-toast'; |
|
|
|
import { useToast } from '../../hooks/use-toast'; |
|
|
|
import { DiscoverRoadmapSorting } from './DiscoverRoadmapSorting'; |
|
|
|
import { DiscoverRoadmapSorting } from './DiscoverRoadmapSorting'; |
|
|
|
|
|
|
|
import { CreateRoadmapModal } from '../CustomRoadmap/CreateRoadmap/CreateRoadmapModal.tsx'; |
|
|
|
|
|
|
|
|
|
|
|
type DiscoverRoadmapsProps = {}; |
|
|
|
type DiscoverRoadmapsProps = {}; |
|
|
|
|
|
|
|
|
|
|
@ -103,90 +104,132 @@ export function DiscoverRoadmaps(props: DiscoverRoadmapsProps) { |
|
|
|
setRoadmapsResponse(response); |
|
|
|
setRoadmapsResponse(response); |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const [isCreatingRoadmap, setIsCreatingRoadmap] = useState(false); |
|
|
|
|
|
|
|
|
|
|
|
const roadmaps = roadmapsResponse?.data || []; |
|
|
|
const roadmaps = roadmapsResponse?.data || []; |
|
|
|
|
|
|
|
|
|
|
|
const loadingIndicator = isLoading && <LoadingRoadmaps />; |
|
|
|
const loadingIndicator = isLoading && <LoadingRoadmaps />; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<section className="container mx-auto py-3 sm:py-6"> |
|
|
|
<> |
|
|
|
<div className="mb-3.5 flex items-stretch justify-between gap-2.5"> |
|
|
|
{isCreatingRoadmap && ( |
|
|
|
<SearchRoadmap |
|
|
|
<CreateRoadmapModal |
|
|
|
total={roadmapsResponse?.totalCount || 0} |
|
|
|
onClose={() => { |
|
|
|
value={pageState.searchTerm} |
|
|
|
setIsCreatingRoadmap(false); |
|
|
|
isLoading={isLoading} |
|
|
|
}} |
|
|
|
onValueChange={(value) => {}} |
|
|
|
/> |
|
|
|
/> |
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
<DiscoverRoadmapSorting |
|
|
|
<div className="border-b bg-white py-7"> |
|
|
|
sortBy={pageState.sortBy} |
|
|
|
<div className="container text-left"> |
|
|
|
onSortChange={(sortBy) => { |
|
|
|
<div className="flex flex-col items-start bg-white"> |
|
|
|
setPageState({ |
|
|
|
<h1 className="mb-1 text-2xl font-bold sm:text-3xl"> |
|
|
|
...pageState, |
|
|
|
Community Roadmaps |
|
|
|
sortBy, |
|
|
|
</h1> |
|
|
|
}); |
|
|
|
<p className="text-base text-gray-500"> |
|
|
|
}} |
|
|
|
Browse the roadmaps created by the community or{' '} |
|
|
|
/> |
|
|
|
<button |
|
|
|
</div> |
|
|
|
onClick={() => { |
|
|
|
|
|
|
|
setIsCreatingRoadmap(true); |
|
|
|
{loadingIndicator} |
|
|
|
}} |
|
|
|
{roadmaps.length === 0 && !isLoading && <EmptyDiscoverRoadmaps />} |
|
|
|
className="rounded text-blue-600 underline" |
|
|
|
{roadmaps.length > 0 && !isLoading && ( |
|
|
|
> |
|
|
|
<> |
|
|
|
create your own roadmap |
|
|
|
<ul className="mb-4 grid grid-cols-1 items-stretch gap-2 sm:grid-cols-2 lg:grid-cols-3"> |
|
|
|
</button> |
|
|
|
{roadmaps.map((roadmap) => { |
|
|
|
|
|
|
|
const roadmapLink = `/r/${roadmap.slug}`; |
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
|
|
<li key={roadmap._id} className="h-full"> |
|
|
|
|
|
|
|
<a |
|
|
|
|
|
|
|
key={roadmap._id} |
|
|
|
|
|
|
|
href={roadmapLink} |
|
|
|
|
|
|
|
className="flex h-full flex-col rounded-md border transition-colors hover:bg-gray-100" |
|
|
|
|
|
|
|
target={'_blank'} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<div className="grow"> |
|
|
|
|
|
|
|
<h2 className="mt-2.5 px-2.5 text-base font-medium leading-tight"> |
|
|
|
|
|
|
|
{roadmap.title} |
|
|
|
|
|
|
|
</h2> |
|
|
|
|
|
|
|
<p className="my-2.5 px-2.5 text-sm text-gray-500"> |
|
|
|
|
|
|
|
{roadmap.description} |
|
|
|
|
|
|
|
</p> |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
<div className="flex items-center justify-between gap-2 px-2.5 py-2"> |
|
|
|
</div> |
|
|
|
<span className="flex items-center gap-1.5 text-xs text-gray-400"> |
|
|
|
<div className="py-3 bg-gray-50"> |
|
|
|
<Shapes size={15} className="inline-block" /> |
|
|
|
<section className="container mx-auto py-3"> |
|
|
|
{Intl.NumberFormat('en-US', { |
|
|
|
<div className="mb-3.5 flex items-stretch justify-between gap-2.5"> |
|
|
|
notation: 'compact', |
|
|
|
<SearchRoadmap |
|
|
|
}).format(roadmap.topicCount)}{' '} |
|
|
|
total={roadmapsResponse?.totalCount || 0} |
|
|
|
topics |
|
|
|
value={pageState.searchTerm} |
|
|
|
</span> |
|
|
|
isLoading={isLoading} |
|
|
|
|
|
|
|
onValueChange={(value) => { |
|
|
|
<Rating |
|
|
|
}} |
|
|
|
rating={roadmap?.ratings?.average || 0} |
|
|
|
/> |
|
|
|
readOnly={true} |
|
|
|
|
|
|
|
starSize={16} |
|
|
|
<DiscoverRoadmapSorting |
|
|
|
|
|
|
|
sortBy={pageState.sortBy} |
|
|
|
|
|
|
|
onSortChange={(sortBy) => { |
|
|
|
|
|
|
|
setPageState({ |
|
|
|
|
|
|
|
...pageState, |
|
|
|
|
|
|
|
sortBy, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}} |
|
|
|
/> |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</a> |
|
|
|
|
|
|
|
</li> |
|
|
|
{loadingIndicator} |
|
|
|
); |
|
|
|
{roadmaps.length === 0 && !isLoading && <EmptyDiscoverRoadmaps/>} |
|
|
|
})} |
|
|
|
{roadmaps.length > 0 && !isLoading && ( |
|
|
|
</ul> |
|
|
|
<> |
|
|
|
|
|
|
|
<ul className="mb-4 grid grid-cols-1 items-stretch gap-3 sm:grid-cols-2 lg:grid-cols-3"> |
|
|
|
<Pagination |
|
|
|
{roadmaps.map((roadmap) => { |
|
|
|
currPage={roadmapsResponse?.currPage || 1} |
|
|
|
const roadmapLink = `/r/${roadmap.slug}`; |
|
|
|
totalPages={roadmapsResponse?.totalPages || 1} |
|
|
|
const totalRatings = Object.keys( |
|
|
|
perPage={roadmapsResponse?.perPage || 0} |
|
|
|
roadmap.ratings?.breakdown || [], |
|
|
|
totalCount={roadmapsResponse?.totalCount || 0} |
|
|
|
).reduce( |
|
|
|
onPageChange={(page) => { |
|
|
|
(acc: number, key: string) => |
|
|
|
setPageState({ |
|
|
|
acc + roadmap.ratings.breakdown[key as any], |
|
|
|
...pageState, |
|
|
|
0, |
|
|
|
currentPage: page, |
|
|
|
); |
|
|
|
}); |
|
|
|
return ( |
|
|
|
}} |
|
|
|
<li key={roadmap._id} className="h-full min-h-[175px]"> |
|
|
|
/> |
|
|
|
<a |
|
|
|
</> |
|
|
|
key={roadmap._id} |
|
|
|
)} |
|
|
|
href={roadmapLink} |
|
|
|
</section> |
|
|
|
className="flex h-full flex-col rounded-lg border bg-white p-3.5 transition-colors hover:border-gray-300 hover:bg-gray-50" |
|
|
|
|
|
|
|
target={'_blank'} |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<div className="grow"> |
|
|
|
|
|
|
|
<h2 className="text-balance text-base font-bold leading-tight"> |
|
|
|
|
|
|
|
{roadmap.title} |
|
|
|
|
|
|
|
</h2> |
|
|
|
|
|
|
|
<p className="mt-2 text-sm text-gray-500"> |
|
|
|
|
|
|
|
{roadmap.description} |
|
|
|
|
|
|
|
</p> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center justify-between gap-2"> |
|
|
|
|
|
|
|
<span className="flex items-center gap-1 text-xs text-gray-400"> |
|
|
|
|
|
|
|
<Shapes size={15} className="inline-block"/> |
|
|
|
|
|
|
|
{Intl.NumberFormat('en-US', { |
|
|
|
|
|
|
|
notation: 'compact', |
|
|
|
|
|
|
|
}).format(roadmap.topicCount)}{' '} |
|
|
|
|
|
|
|
</span> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<Rating |
|
|
|
|
|
|
|
rating={roadmap?.ratings?.average || 0} |
|
|
|
|
|
|
|
readOnly={true} |
|
|
|
|
|
|
|
starSize={16} |
|
|
|
|
|
|
|
total={totalRatings} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</a> |
|
|
|
|
|
|
|
</li> |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
})} |
|
|
|
|
|
|
|
</ul> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<Pagination |
|
|
|
|
|
|
|
currPage={roadmapsResponse?.currPage || 1} |
|
|
|
|
|
|
|
totalPages={roadmapsResponse?.totalPages || 1} |
|
|
|
|
|
|
|
perPage={roadmapsResponse?.perPage || 0} |
|
|
|
|
|
|
|
totalCount={roadmapsResponse?.totalCount || 0} |
|
|
|
|
|
|
|
onPageChange={(page) => { |
|
|
|
|
|
|
|
setPageState({ |
|
|
|
|
|
|
|
...pageState, |
|
|
|
|
|
|
|
currentPage: page, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
}} |
|
|
|
|
|
|
|
/> |
|
|
|
|
|
|
|
</> |
|
|
|
|
|
|
|
)} |
|
|
|
|
|
|
|
</section> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</> |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|