/** * GET /api/plants/suggestions?q= * * Returns autocomplete suggestions for the navbar search-as-you-type feature. * Queries both plants and diseases from the database and returns an interleaved * list with at most 8 suggestions total. * * Each suggestion includes: type (plant|disease), id, label, subtitle, emoji, href. * Plants link to their browse detail page; diseases link to the plant page with * a hash anchor to the specific disease card. */ import { NextRequest, NextResponse } from "next/server"; import { like, or, eq } from "drizzle-orm"; import { getDb } from "@/lib/db/index"; import { plants, diseases } from "@/lib/db/schema"; import { getEmojiForCategory } from "@/lib/display-helpers"; export const dynamic = "force-dynamic"; interface SuggestionItem { type: "plant" | "disease"; id: string; label: string; subtitle: string; emoji: string; href: string; } export async function GET(request: NextRequest) { const q = request.nextUrl.searchParams.get("q")?.trim() ?? ""; // Empty or very short queries return no suggestions if (q.length < 1) { return NextResponse.json({ suggestions: [] }); } const db = getDb(); const term = `%${q.toLowerCase()}%`; // Fetch matching plants (by common name or scientific name) const plantRows = await db .select({ id: plants.id, commonName: plants.commonName, scientificName: plants.scientificName, category: plants.category, }) .from(plants) .where(or(like(plants.commonName, term), like(plants.scientificName, term))) .limit(5); // Fetch matching diseases (by name or scientific name) with parent plant info const diseaseRows = await db .select({ id: diseases.id, name: diseases.name, plantId: diseases.plantId, plantCommonName: plants.commonName, plantCategory: plants.category, }) .from(diseases) .leftJoin(plants, eq(diseases.plantId, plants.id)) .where(or(like(diseases.name, term), like(diseases.scientificName, term))) .limit(5); const plantSuggestions: SuggestionItem[] = plantRows.map((p) => ({ type: "plant" as const, id: p.id, label: p.commonName, subtitle: p.scientificName, emoji: getEmojiForCategory(p.category), href: `/browse/${p.id}`, })); const diseaseSuggestions: SuggestionItem[] = diseaseRows.map((d) => ({ type: "disease" as const, id: d.id, label: d.name, subtitle: `Disease on ${d.plantCommonName ?? "Unknown plant"}`, emoji: getEmojiForCategory(d.plantCategory ?? "houseplant"), href: `/browse/${d.plantId}#disease-${d.id}`, })); // Interleave plant and disease results so the dropdown shows variety const interleaved: SuggestionItem[] = []; const maxLen = Math.max(plantSuggestions.length, diseaseSuggestions.length); for (let i = 0; i < maxLen && interleaved.length < 8; i++) { if (i < plantSuggestions.length) { interleaved.push(plantSuggestions[i]); } if (i < diseaseSuggestions.length && interleaved.length < 8) { interleaved.push(diseaseSuggestions[i]); } } return NextResponse.json({ suggestions: interleaved }); }