feat: Added popular topics pill-link section to homepage that fetches f…

- "frontend/src/pages/Home.tsx"
- "frontend/src/App.css"

GSD-Task: S01/T02
This commit is contained in:
jlightner 2026-03-31 05:37:10 +00:00
parent 881629b5c3
commit 5f7a8a6f77
2 changed files with 83 additions and 0 deletions

View file

@ -953,6 +953,50 @@ a.app-footer__repo:hover {
transform: translateY(-1px); transform: translateY(-1px);
} }
/* ── Popular topics quick-links ───────────────────────────────────────────── */
.home-popular-topics {
margin-top: 2.5rem;
text-align: center;
}
.home-popular-topics__title {
font-size: 0.875rem;
font-weight: 600;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.75rem;
}
.home-popular-topics__list {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
max-width: 36rem;
margin: 0 auto;
}
.pill--topic-quick {
display: inline-block;
padding: 0.375rem 0.875rem;
border-radius: 9999px;
font-size: 0.8125rem;
font-weight: 500;
background: var(--color-pill-bg);
color: var(--color-pill-text);
border: 1px solid var(--color-border);
text-decoration: none;
transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.pill--topic-quick:hover {
border-color: var(--color-accent);
background: var(--color-accent-subtle);
color: var(--color-accent);
}
/* ── Search form ──────────────────────────────────────────────────────────── */ /* ── Search form ──────────────────────────────────────────────────────────── */
.search-container { .search-container {

View file

@ -11,6 +11,7 @@ import { Link, useNavigate } from "react-router-dom";
import { import {
searchApi, searchApi,
fetchTechniques, fetchTechniques,
fetchTopics,
type SearchResultItem, type SearchResultItem,
type TechniqueListItem, type TechniqueListItem,
} from "../api/public-client"; } from "../api/public-client";
@ -21,6 +22,7 @@ export default function Home() {
const [showDropdown, setShowDropdown] = useState(false); const [showDropdown, setShowDropdown] = useState(false);
const [recent, setRecent] = useState<TechniqueListItem[]>([]); const [recent, setRecent] = useState<TechniqueListItem[]>([]);
const [recentLoading, setRecentLoading] = useState(true); const [recentLoading, setRecentLoading] = useState(true);
const [popularTopics, setPopularTopics] = useState<{name: string; count: number}[]>([]);
const navigate = useNavigate(); const navigate = useNavigate();
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null); const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@ -49,6 +51,26 @@ export default function Home() {
}; };
}, []); }, []);
// Load popular topics
useEffect(() => {
let cancelled = false;
void (async () => {
try {
const categories = await fetchTopics();
const all = categories.flatMap((cat) =>
cat.sub_topics.map((st) => ({ name: st.name, count: st.technique_count }))
);
all.sort((a, b) => b.count - a.count);
if (!cancelled) setPopularTopics(all.slice(0, 8));
} catch {
// optional section — silently ignore
}
})();
return () => {
cancelled = true;
};
}, []);
// Close dropdown on outside click // Close dropdown on outside click
useEffect(() => { useEffect(() => {
function handleClick(e: MouseEvent) { function handleClick(e: MouseEvent) {
@ -198,6 +220,23 @@ export default function Home() {
</div> </div>
<Link to="/topics" className="btn home-cta">Start Exploring</Link> <Link to="/topics" className="btn home-cta">Start Exploring</Link>
{popularTopics.length > 0 && (
<section className="home-popular-topics">
<h2 className="home-popular-topics__title">Popular Topics</h2>
<div className="home-popular-topics__list">
{popularTopics.map((topic) => (
<Link
key={topic.name}
to={`/search?q=${encodeURIComponent(topic.name)}&scope=topics`}
className="pill pill--topic-quick"
>
{topic.name}
</Link>
))}
</div>
</section>
)}
</section> </section>
{/* Navigation cards */} {/* Navigation cards */}