diff --git a/app/(dashboard)/skills/_components/skills-library.tsx b/app/(dashboard)/skills/_components/skills-library.tsx new file mode 100644 index 0000000..d303f9c --- /dev/null +++ b/app/(dashboard)/skills/_components/skills-library.tsx @@ -0,0 +1,123 @@ +'use client' + +import { useState, useTransition } from 'react' +import { MdBolt, MdCheckCircle, MdOpenInNew } from 'react-icons/md' +import { useRouter } from 'next/navigation' +import { toast } from 'sonner' + +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card' +import { enableGlobalSkill } from '@/lib/actions/skill' +import { getSkillCatalog } from '@/lib/skills/catalog' + +type SkillsLibraryProps = { + enabledSkillIds: string[] +} + +export function SkillsLibrary({ enabledSkillIds }: SkillsLibraryProps) { + const router = useRouter() + const [pendingSkillId, setPendingSkillId] = useState(null) + const [isPending, startTransition] = useTransition() + const catalog = getSkillCatalog() + const enabledSkillSet = new Set(enabledSkillIds) + + const handleEnable = (skillId: string) => { + startTransition(async () => { + setPendingSkillId(skillId) + + const result = await enableGlobalSkill(skillId) + if (!result.success) { + toast.error(result.error) + setPendingSkillId(null) + return + } + + toast.success('Global skill enabled. Install tasks will fan out across your projects.') + router.refresh() + setPendingSkillId(null) + }) + } + + return ( +
+
+ + Global Skills + +
+

Skills

+

+ Enabling a skill here creates global desired state for the user. Existing projects get + `INSTALL_SKILL` tasks immediately, and new projects inherit the same skill when they + are created. +

+
+
+ +
+ {catalog.map((skill) => { + const isEnabled = enabledSkillSet.has(skill.skillId) + const isLoading = isPending && pendingSkillId === skill.skillId + + return ( + + +
+
+ +
+ + {isEnabled ? 'Enabled' : 'Available'} + +
+
+ {skill.name} + {skill.description} +
+
+ + +
+
+ + Install Command +
+ + {skill.installCommand} + +
+
+ + + + + + +
+ ) + })} +
+
+ ) +} diff --git a/app/(dashboard)/skills/layout.tsx b/app/(dashboard)/skills/layout.tsx new file mode 100644 index 0000000..bb6b758 --- /dev/null +++ b/app/(dashboard)/skills/layout.tsx @@ -0,0 +1,14 @@ +import { Sidebar } from '@/components/sidebar' + +export default function SkillsLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( +
+ +
{children}
+
+ ) +} diff --git a/app/(dashboard)/skills/page.tsx b/app/(dashboard)/skills/page.tsx new file mode 100644 index 0000000..cede1ac --- /dev/null +++ b/app/(dashboard)/skills/page.tsx @@ -0,0 +1,29 @@ +import { redirect } from 'next/navigation' + +import { auth } from '@/lib/auth' +import { getUserSkills } from '@/lib/data/user-skill' + +import { SkillsLibrary } from './_components/skills-library' + +export const metadata = { + title: 'Skills | Fulling', + description: 'Enable global skills that will be installed across your projects.', +} + +export default async function SkillsPage() { + const session = await auth() + + if (!session) { + redirect('/login') + } + + const userSkills = await getUserSkills(session.user.id) + + return ( +
+ skill.skillId)} + /> +
+ ) +} diff --git a/components/sidebar.tsx b/components/sidebar.tsx index 9bf08f4..e99d9bb 100644 --- a/components/sidebar.tsx +++ b/components/sidebar.tsx @@ -1,3 +1,5 @@ +'use client' + import { FaGithub } from 'react-icons/fa6' import { MdDashboardCustomize, @@ -10,6 +12,7 @@ import { } from 'react-icons/md' import Image from 'next/image' import Link from 'next/link' +import { usePathname } from 'next/navigation' import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' @@ -61,6 +64,8 @@ function LogoSection() { } function NavMenu() { + const pathname = usePathname() + return (