feat: Added featured technique spotlight section and converted recently…

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

GSD-Task: S03/T02
This commit is contained in:
jlightner 2026-03-31 05:48:48 +00:00
parent 810f96a640
commit 348089d635
2 changed files with 136 additions and 7 deletions

View file

@ -1173,8 +1173,78 @@ a.app-footer__repo:hover {
/* ── Recently Added section ───────────────────────────────────────────────── */
/* ── Featured technique spotlight ──────────────────────────────────────── */
.home-featured {
max-width: 42rem;
margin: 0 auto 1.5rem;
padding: 1.25rem 1.5rem;
background: var(--color-bg-surface);
border: 1px solid var(--color-border);
border-left: 3px solid var(--color-accent);
border-radius: 0.5rem;
text-align: left;
}
.home-featured__label {
font-size: 0.6875rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-accent);
margin-bottom: 0.5rem;
}
.home-featured__title {
display: block;
font-size: 1.25rem;
font-weight: 700;
color: var(--color-text);
text-decoration: none;
margin-bottom: 0.5rem;
line-height: 1.3;
}
.home-featured__title:hover {
color: var(--color-accent-hover);
}
.home-featured__summary {
font-size: 0.875rem;
color: var(--color-text-secondary);
line-height: 1.5;
margin-bottom: 0.75rem;
}
.home-featured__meta {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
margin-bottom: 0.5rem;
}
.home-featured__moments {
font-size: 0.75rem;
color: var(--color-text-tertiary);
white-space: nowrap;
}
.home-featured__creator {
display: block;
font-size: 0.8125rem;
color: var(--color-text-secondary);
text-decoration: none;
}
.home-featured__creator:hover {
color: var(--color-accent-hover);
}
/* ── Recently added ───────────────────────────────────────────────────── */
.recent-section {
max-width: 36rem;
max-width: 42rem;
margin: 0 auto 2rem;
}
@ -1185,8 +1255,8 @@ a.app-footer__repo:hover {
}
.recent-list {
display: flex;
flex-direction: column;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.5rem;
}
@ -2209,6 +2279,14 @@ a.app-footer__repo:hover {
grid-template-columns: 1fr;
}
.recent-list {
grid-template-columns: 1fr;
}
.home-featured {
padding: 1rem 1.25rem;
}
.technique-header__title {
font-size: 1.375rem;
}

View file

@ -20,6 +20,7 @@ export default function Home() {
const [query, setQuery] = useState("");
const [suggestions, setSuggestions] = useState<SearchResultItem[]>([]);
const [showDropdown, setShowDropdown] = useState(false);
const [featured, setFeatured] = useState<TechniqueListItem | null>(null);
const [recent, setRecent] = useState<TechniqueListItem[]>([]);
const [recentLoading, setRecentLoading] = useState(true);
const [popularTopics, setPopularTopics] = useState<{name: string; count: number}[]>([]);
@ -33,12 +34,28 @@ export default function Home() {
inputRef.current?.focus();
}, []);
// Load featured technique (random)
useEffect(() => {
let cancelled = false;
void (async () => {
try {
const res = await fetchTechniques({ sort: "random", limit: 1 });
if (!cancelled && res.items.length > 0) setFeatured(res.items[0]);
} catch {
// silently ignore — optional section
}
})();
return () => {
cancelled = true;
};
}, []);
// Load recently added techniques
useEffect(() => {
let cancelled = false;
void (async () => {
try {
const res = await fetchTechniques({ limit: 5 });
const res = await fetchTechniques({ sort: "recent", limit: 6 });
if (!cancelled) setRecent(res.items);
} catch {
// silently ignore — not critical
@ -255,6 +272,37 @@ export default function Home() {
</Link>
</section>
{/* Featured Technique Spotlight */}
{featured && (
<section className="home-featured">
<h3 className="home-featured__label">Featured Technique</h3>
<Link to={`/techniques/${featured.slug}`} className="home-featured__title">
{featured.title}
</Link>
{featured.summary && (
<p className="home-featured__summary">{featured.summary}</p>
)}
<div className="home-featured__meta">
{featured.topic_category && (
<span className="badge badge--category">{featured.topic_category}</span>
)}
{featured.topic_tags && featured.topic_tags.length > 0 && featured.topic_tags.map(tag => (
<span key={tag} className="pill pill--tag">{tag}</span>
))}
{featured.key_moment_count > 0 && (
<span className="home-featured__moments">
{featured.key_moment_count} moment{featured.key_moment_count !== 1 ? "s" : ""}
</span>
)}
</div>
{featured.creator_name && (
<Link to={`/creators/${featured.creator_slug}`} className="home-featured__creator">
by {featured.creator_name}
</Link>
)}
</section>
)}
{/* Recently Added */}
<section className="recent-section">
<h3 className="recent-section__title">Recently Added</h3>
@ -264,7 +312,10 @@ export default function Home() {
<div className="empty-state">No techniques yet.</div>
) : (
<div className="recent-list">
{recent.map((t) => (
{recent
.filter((t) => t.id !== featured?.id)
.slice(0, 4)
.map((t) => (
<Link
key={t.id}
to={`/techniques/${t.slug}`}
@ -285,8 +336,8 @@ export default function Home() {
))}
{t.summary && (
<span className="recent-card__summary">
{t.summary.length > 100
? `${t.summary.slice(0, 100)}`
{t.summary.length > 150
? `${t.summary.slice(0, 150)}`
: t.summary}
</span>
)}