mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-31 13:01:43 +02:00
Add roadmaps page
This commit is contained in:
@@ -21,7 +21,7 @@ const discordInfo = await getDiscordInfo();
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class='mt-5 grid grid-cols-1 justify-between gap-2 divide-x-0 sm:my-11 sm:grid-cols-3 sm:gap-0 sm:divide-x'
|
class='mt-5 grid grid-cols-1 justify-between gap-2 divide-x-0 sm:my-11 sm:grid-cols-3 sm:gap-0 sm:divide-x mb-4 sm:mb-0'
|
||||||
>
|
>
|
||||||
<OpenSourceStat text='GitHub Stars' value={starCount} />
|
<OpenSourceStat text='GitHub Stars' value={starCount} />
|
||||||
<OpenSourceStat text='Registered Users' value={'850k'} />
|
<OpenSourceStat text='Registered Users' value={'850k'} />
|
||||||
|
@@ -14,7 +14,7 @@ const isDiscordMembers = text.toLowerCase() === 'discord members';
|
|||||||
---
|
---
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class='flex items-start sm:items-center justify-start flex-col sm:justify-center sm:gap-0 gap-2 sm:bg-transparent bg-gray-200 sm:rounded-none rounded-xl p-4'
|
class='flex items-start sm:items-center justify-start flex-col sm:justify-center sm:gap-0 gap-2 sm:bg-transparent sm:rounded-none rounded-xl p-0 sm:p-4 mb-3 sm:mb-0'
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
isGitHubStars && (
|
isGitHubStars && (
|
||||||
|
28
src/components/Roadmaps/CategoryFilterButton.tsx
Normal file
28
src/components/Roadmaps/CategoryFilterButton.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { cn } from '../../lib/classname.ts';
|
||||||
|
|
||||||
|
type CategoryFilterButtonProps = {
|
||||||
|
category: string;
|
||||||
|
selected: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CategoryFilterButton(props: CategoryFilterButtonProps) {
|
||||||
|
const { category, selected, onClick } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={cn(
|
||||||
|
'border-b bg-gradient-to-l py-1.5 pr-3 text-center text-sm text-gray-500 hover:from-white hover:text-gray-900 sm:text-right',
|
||||||
|
{
|
||||||
|
'from-white font-semibold text-gray-900':
|
||||||
|
selected && category !== 'All Roadmaps',
|
||||||
|
'font-semibold text-gray-900': selected && category === 'All Roadmaps',
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
type="button"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,5 +1,8 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { cn } from '../../lib/classname.ts';
|
import { cn } from '../../lib/classname.ts';
|
||||||
|
import { Filter, X } from 'lucide-react';
|
||||||
|
import { CategoryFilterButton } from './CategoryFilterButton.tsx';
|
||||||
|
import { useOutsideClick } from '../../hooks/use-outside-click.ts';
|
||||||
|
|
||||||
const groupNames = [
|
const groupNames = [
|
||||||
'Absolute Beginners',
|
'Absolute Beginners',
|
||||||
@@ -387,6 +390,14 @@ export function RoadmapsPage() {
|
|||||||
const [activeGroup, setActiveGroup] = useState<AllowGroupNames>('');
|
const [activeGroup, setActiveGroup] = useState<AllowGroupNames>('');
|
||||||
const [visibleGroups, setVisibleGroups] = useState<GroupType[]>(allGroups);
|
const [visibleGroups, setVisibleGroups] = useState<GroupType[]>(allGroups);
|
||||||
|
|
||||||
|
const [isFilterOpen, setIsFilterOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isFilterOpen) {
|
||||||
|
document?.getElementById('filter-button')?.focus();
|
||||||
|
}
|
||||||
|
}, [isFilterOpen]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!activeGroup) {
|
if (!activeGroup) {
|
||||||
setVisibleGroups(allGroups);
|
setVisibleGroups(allGroups);
|
||||||
@@ -421,46 +432,65 @@ export function RoadmapsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-t bg-gray-100">
|
<div className="border-t bg-gray-100">
|
||||||
<div className="container flex flex-row gap-4">
|
|
||||||
<div className="w-[180px] border-r bg-gradient-to-l from-gray-100 pt-6">
|
|
||||||
<div className="sticky top-10 pb-20">
|
|
||||||
<div className="grid grid-cols-1">
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveGroup('')}
|
onClick={() => {
|
||||||
className={cn('border-b py-1.5 pr-3 text-right text-sm', {
|
setIsFilterOpen(!isFilterOpen);
|
||||||
'font-bold text-gray-900': activeGroup === '',
|
}}
|
||||||
})}
|
id="filter-button"
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
All Roadmaps
|
|
||||||
</button>
|
|
||||||
{groups.map((group) => (
|
|
||||||
<button
|
|
||||||
key={group.group}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'border-b bg-gradient-to-l py-1.5 pr-3 text-right text-sm text-gray-500 hover:from-white hover:text-gray-900',
|
'-mt-1 flex w-full items-center justify-center bg-gray-300 py-2 text-sm text-black focus:shadow-none focus:outline-0 sm:hidden',
|
||||||
{
|
{
|
||||||
'from-white font-semibold text-gray-900':
|
'mb-3': !isFilterOpen,
|
||||||
activeGroup === group.group,
|
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
type="button"
|
|
||||||
onClick={() => setActiveGroup(group.group)}
|
|
||||||
>
|
>
|
||||||
{group.group}
|
{!isFilterOpen && <Filter size={13} className="mr-1" />}
|
||||||
|
{isFilterOpen && <X size={13} className="mr-1" />}
|
||||||
|
Categories
|
||||||
</button>
|
</button>
|
||||||
|
<div className="container relative flex flex-col gap-4 sm:flex-row">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'hidden w-full flex-col from-gray-100 sm:w-[180px] sm:border-r sm:bg-gradient-to-l sm:pt-6',
|
||||||
|
{
|
||||||
|
'hidden sm:flex': !isFilterOpen,
|
||||||
|
'z-50 flex': isFilterOpen,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 -mx-4 w-full bg-white pb-0 shadow-xl sm:sticky sm:top-10 sm:mx-0 sm:bg-transparent sm:pb-20 sm:shadow-none">
|
||||||
|
<div className="grid grid-cols-1">
|
||||||
|
<CategoryFilterButton
|
||||||
|
onClick={() => {
|
||||||
|
setActiveGroup('');
|
||||||
|
setIsFilterOpen(false);
|
||||||
|
}}
|
||||||
|
category={'All Roadmaps'}
|
||||||
|
selected={activeGroup === ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{groups.map((group) => (
|
||||||
|
<CategoryFilterButton
|
||||||
|
key={group.group}
|
||||||
|
onClick={() => {
|
||||||
|
setActiveGroup(group.group);
|
||||||
|
setIsFilterOpen(false);
|
||||||
|
}}
|
||||||
|
category={group.group}
|
||||||
|
selected={activeGroup === group.group}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-grow flex-col gap-6 pb-20 pt-8">
|
<div className="flex flex-grow flex-col gap-6 pb-20 pt-2 sm:pt-8">
|
||||||
{visibleGroups.map((group) => (
|
{visibleGroups.map((group) => (
|
||||||
<div key={`${group.group}-${group.roadmaps.length}`}>
|
<div key={`${group.group}-${group.roadmaps.length}`}>
|
||||||
<h2 className="mb-2 text-xs uppercase tracking-wide text-gray-400">
|
<h2 className="mb-2 text-xs uppercase tracking-wide text-gray-400">
|
||||||
{group.group}
|
{group.group}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 gap-1.5">
|
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2 md:grid-cols-3">
|
||||||
{group.roadmaps.map((roadmap) => (
|
{group.roadmaps.map((roadmap) => (
|
||||||
<a
|
<a
|
||||||
key={roadmap.link}
|
key={roadmap.link}
|
||||||
|
@@ -1,21 +1,21 @@
|
|||||||
export function RoadmapsPageHeader() {
|
export function RoadmapsPageHeader() {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white py-12">
|
<div className="bg-white py-3 sm:py-12">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="flex flex-col items-center bg-white">
|
<div className="flex flex-col items-start sm:items-center bg-white">
|
||||||
<h1 className="text-5xl font-bold">Developer Roadmaps</h1>
|
<h1 className="text-2xl sm:text-5xl font-bold">Developer Roadmaps</h1>
|
||||||
<p className="my-3 text-lg">
|
<p className="mb-3 mt-1 sm:my-3 text-sm sm:text-lg">
|
||||||
Browse ever-growing list of up-to-date, community driven roadmaps
|
Browse the ever-growing list of up-to-date, community driven roadmaps
|
||||||
</p>
|
</p>
|
||||||
<p className="flex flex-row gap-3">
|
<p className="flex flex-col sm:flex-row gap-1.5 sm:gap-3 w-full sm:w-auto mb-3 sm:mb-0">
|
||||||
<a
|
<a
|
||||||
className="inline-block rounded-md bg-black px-3.5 py-1.5 text-base text-white"
|
className="inline-block rounded-md bg-black px-3.5 py-2 sm:py-1.5 text-sm sm:text-base text-white"
|
||||||
href="#"
|
href="#"
|
||||||
>
|
>
|
||||||
Draw your own roadmap
|
Draw your own roadmap
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
className="inline-block rounded-md bg-gray-300 px-3.5 py-1.5 text-base text-black"
|
className="inline-block rounded-md bg-gray-300 px-3.5 py-2 sm:py-1.5 text-sm sm:text-base text-black"
|
||||||
href="#"
|
href="#"
|
||||||
>
|
>
|
||||||
Generate Roadmap with AI
|
Generate Roadmap with AI
|
||||||
|
Reference in New Issue
Block a user