Add a global search feature to find articles and videos
Introduces a global search functionality with a dedicated search page and integrates a search icon/input into the header and mobile menu. The search queries articles by title, excerpt, and content, and videos by title and description. Updates are made to `App.tsx` to include the new route, and `header.tsx` to implement the search UI and logic. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 517dfa7b-26ac-463d-a6e1-a58c6df97188 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 90e58e4e-8d41-41e7-bc45-74196314bd78 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/517dfa7b-26ac-463d-a6e1-a58c6df97188/jdAEdU5 Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -90,6 +90,50 @@ export async function registerRoutes(
|
||||
res.json(articles);
|
||||
});
|
||||
|
||||
app.get("/api/search", async (req, res) => {
|
||||
const q = (req.query.q as string || "").trim().toLowerCase();
|
||||
if (!q || q.length < 2) return res.json({ articles: [], videos: [] });
|
||||
|
||||
try {
|
||||
const articles = await storage.getArticles();
|
||||
const matchedArticles = articles.filter(a =>
|
||||
a.title.toLowerCase().includes(q) ||
|
||||
a.excerpt.toLowerCase().includes(q) ||
|
||||
a.content.replace(/<[^>]+>/g, "").toLowerCase().includes(q) ||
|
||||
a.category.toLowerCase().includes(q)
|
||||
).slice(0, 10);
|
||||
|
||||
const cacheKey = `videos_1_100_`;
|
||||
let videos: any[] = [];
|
||||
const cached = getCached<any>(cacheKey, 30 * 60 * 1000);
|
||||
if (cached) {
|
||||
videos = cached.items || [];
|
||||
} else {
|
||||
try {
|
||||
const data = await bunnyFetch(`/library/${LIBRARY_ID}/videos?page=1&itemsPerPage=100&orderBy=date`);
|
||||
videos = (data.items || []).map((v: any) => {
|
||||
const descTag = (v.metaTags || []).find((t: any) => t.property === "description");
|
||||
return {
|
||||
guid: v.guid,
|
||||
title: (v.title || "").replace(/\.mp4$/i, ""),
|
||||
description: descTag?.value || v.description || "",
|
||||
thumbnail: `https://${CDN_HOST}/${v.guid}/${v.thumbnailFileName || "thumbnail.jpg"}`,
|
||||
embedUrl: `https://player.mediadelivery.net/embed/${LIBRARY_ID}/${v.guid}`,
|
||||
};
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
const matchedVideos = videos.filter((v: any) =>
|
||||
v.title.toLowerCase().includes(q) ||
|
||||
(v.description || "").toLowerCase().includes(q)
|
||||
).slice(0, 10);
|
||||
|
||||
res.json({ articles: matchedArticles, videos: matchedVideos });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/articles/:slug", async (req, res) => {
|
||||
const article = await storage.getArticleBySlug(req.params.slug);
|
||||
if (!article) {
|
||||
|
||||
Reference in New Issue
Block a user