Skip to content

Commit 1d4f46e

Browse files
committed
Group homepage skill list by plugin instead of category
- SkillList now groups and filters by plugin (Market Analysis, Social Readers, Data Providers, Startup Tools, UI Tools) - Filter pills switch from category to plugin-based - Each skill row shows its category as a subtle tag - Skill detail page sidebar and tag links updated to use ?plugin=
1 parent e3c06f7 commit 1d4f46e

2 files changed

Lines changed: 38 additions & 41 deletions

File tree

apps/web/src/app/skill-list.tsx

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,48 @@ import { useState } from "react";
44
import { useSearchParams } from "next/navigation";
55
import { Link } from "next-view-transitions";
66
import { motion, AnimatePresence, LayoutGroup } from "motion/react";
7-
import type { Skill, SkillCategory } from "@/data/skills";
8-
import { categoryLabels } from "@/data/skills";
7+
import type { Skill, PluginGroup } from "@/data/skills";
8+
import { pluginGroupLabels, categoryLabels } from "@/data/skills";
99

10-
const categoryOrder: SkillCategory[] = [
11-
"analysis",
12-
"data",
13-
"risk",
14-
"sentiment",
15-
"strategy",
16-
"visualization",
10+
const pluginOrder: PluginGroup[] = [
11+
"market-analysis",
12+
"social-readers",
13+
"data-providers",
14+
"startup-tools",
15+
"ui-tools",
1716
];
1817

19-
type CategoryFilter = "all" | SkillCategory;
18+
type PluginFilter = "all" | PluginGroup;
2019

21-
const categoryFilters: { value: CategoryFilter; label: string }[] = [
20+
const pluginFilters: { value: PluginFilter; label: string }[] = [
2221
{ value: "all", label: "All" },
23-
...categoryOrder.map((cat) => ({
24-
value: cat as CategoryFilter,
25-
label: categoryLabels[cat],
22+
...pluginOrder.map((p) => ({
23+
value: p as PluginFilter,
24+
label: pluginGroupLabels[p],
2625
})),
2726
];
2827

29-
function isValidCategory(value: string | null): value is SkillCategory {
30-
return value !== null && categoryOrder.includes(value as SkillCategory);
28+
function isValidPlugin(value: string | null): value is PluginGroup {
29+
return value !== null && pluginOrder.includes(value as PluginGroup);
3130
}
3231

3332
export function SkillList({ skills }: { skills: Skill[] }) {
3433
const searchParams = useSearchParams();
35-
const initialCategory = searchParams.get("category");
36-
const [activeFilter, setActiveFilter] = useState<CategoryFilter>(
37-
isValidCategory(initialCategory) ? initialCategory : "all"
34+
const initialPlugin = searchParams.get("plugin");
35+
const [activeFilter, setActiveFilter] = useState<PluginFilter>(
36+
isValidPlugin(initialPlugin) ? initialPlugin : "all"
3837
);
3938

4039
const filtered =
4140
activeFilter === "all"
4241
? skills
43-
: skills.filter((s) => s.category === activeFilter);
42+
: skills.filter((s) => s.plugin === activeFilter);
4443

45-
const grouped = categoryOrder
46-
.map((cat) => ({
47-
category: cat,
48-
label: categoryLabels[cat],
49-
skills: filtered.filter((s) => s.category === cat),
44+
const grouped = pluginOrder
45+
.map((p) => ({
46+
plugin: p,
47+
label: pluginGroupLabels[p],
48+
skills: filtered.filter((s) => s.plugin === p),
5049
}))
5150
.filter((g) => g.skills.length > 0);
5251

@@ -56,7 +55,7 @@ export function SkillList({ skills }: { skills: Skill[] }) {
5655
<div className="sticky top-0 z-20 bg-bg/80 backdrop-blur-md flex items-center gap-3 py-3 -mx-6 px-6">
5756
<LayoutGroup>
5857
<div className="relative flex items-center gap-2 flex-wrap">
59-
{categoryFilters.map((f) => (
58+
{pluginFilters.map((f) => (
6059
<button
6160
key={f.value}
6261
onClick={() => setActiveFilter(f.value)}
@@ -88,12 +87,12 @@ export function SkillList({ skills }: { skills: Skill[] }) {
8887
</motion.span>
8988
</div>
9089

91-
{/* Category sections */}
90+
{/* Plugin sections */}
9291
<div className="pb-16">
9392
<AnimatePresence mode="popLayout" initial={false}>
9493
{grouped.map((group) => (
9594
<motion.section
96-
key={group.category}
95+
key={group.plugin}
9796
layout
9897
initial={{ opacity: 0, y: 8 }}
9998
animate={{ opacity: 1, y: 0 }}
@@ -126,6 +125,9 @@ export function SkillList({ skills }: { skills: Skill[] }) {
126125
<span className="font-medium text-sm group-hover:text-accent transition-colors">
127126
{skill.name}
128127
</span>
128+
<span className="text-[10px] border border-border rounded px-1.5 py-0.5 text-text-muted">
129+
{categoryLabels[skill.category]}
130+
</span>
129131
{skill.badge === "new" && (
130132
<span className="text-[10px] font-semibold uppercase tracking-wider bg-accent/15 text-accent px-1.5 py-0.5 rounded">
131133
New

apps/web/src/app/skills/[name]/page.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,15 @@ export default async function SkillDetailPage({
9292

9393
<div className="flex gap-2 flex-wrap">
9494
<Link
95-
href={`/?category=${skill.category}`}
95+
href={`/?plugin=${skill.plugin}`}
9696
scroll={false}
9797
className="text-xs border border-border rounded px-2 py-0.5 text-text-secondary hover:border-accent hover:text-accent transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
9898
>
99-
{categoryLabels[skill.category]}
99+
{pluginGroupLabels[skill.plugin]}
100100
</Link>
101+
<span className="text-xs border border-border rounded px-2 py-0.5 text-text-secondary">
102+
{categoryLabels[skill.category]}
103+
</span>
101104
{skill.tags.map((tag) => (
102105
<span
103106
key={tag}
@@ -133,23 +136,15 @@ export default async function SkillDetailPage({
133136
<div className="lg:w-56 shrink-0 space-y-6">
134137
<div>
135138
<p className="text-xs uppercase tracking-wider text-text-muted mb-1.5">
136-
Category
139+
Plugin
137140
</p>
138141
<Link
139-
href={`/?category=${skill.category}`}
142+
href={`/?plugin=${skill.plugin}`}
140143
scroll={false}
141144
className="text-sm text-accent hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent rounded"
142145
>
143-
{categoryLabels[skill.category]}
144-
</Link>
145-
</div>
146-
<div>
147-
<p className="text-xs uppercase tracking-wider text-text-muted mb-1.5">
148-
Plugin
149-
</p>
150-
<span className="text-sm text-text-secondary">
151146
{pluginGroupLabels[skill.plugin]}
152-
</span>
147+
</Link>
153148
</div>
154149
<div>
155150
<p className="text-xs uppercase tracking-wider text-text-muted mb-1.5">

0 commit comments

Comments
 (0)