feat(courses): add type, search, and pricing filters to org landing page

- Add Types section to the Filter sidebar with all 4 course types
- Add full-text search input centered in the hero (title + description)
- Add Pricing section (Free / Paid) based on course cost
- Compact hero prop across all 10 themes to halve vertical padding
- Children snippet support in all hero themes for the search input
- All filters are URL-driven (?types=, ?search=, ?tags=, ?pricing=)
- Full-stack: validation schema, DB queries, service, API route, server load, UI
This commit is contained in:
Rotimi Best
2026-07-01 10:36:53 +01:00
parent 8d2f2a6d2d
commit 78a2b85ad3
27 changed files with 464 additions and 97 deletions
@@ -383,15 +383,21 @@ export const organizationRouter = new Hono()
*/
.get('/courses/public', zValidator('query', ZGetCoursesBySiteName), async (c) => {
try {
const { siteName, tags, page, limit } = c.req.valid('query');
const tagSlugs = tags
?.split(',')
.map((value) => value.trim())
.filter(Boolean);
const { siteName, tags, types, search, pricing, page, limit } = c.req.valid('query');
const splitParam = (param: string | undefined) =>
param
?.split(',')
.map((v) => v.trim())
.filter(Boolean);
const tagSlugs = splitParam(tags);
const courseTypes = splitParam(types);
const pagination = page !== undefined || limit !== undefined ? { page, limit } : undefined;
const courses = await getPublicCourses(
siteName,
tagSlugs && tagSlugs.length > 0 ? tagSlugs : undefined,
tagSlugs?.length ? tagSlugs : undefined,
courseTypes?.length ? courseTypes : undefined,
search || undefined,
pricing || undefined,
pagination
);