search, db integration
This commit is contained in:
94
apps/web/src/lib/api/browse.ts
Normal file
94
apps/web/src/lib/api/browse.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Browse API — fetches plants with disease counts from the Turso DB
|
||||
* for the browse page. Runs server-side only.
|
||||
*/
|
||||
|
||||
import { sql, eq } from "drizzle-orm";
|
||||
import { getDb } from "@/lib/db/index";
|
||||
import { plants, diseases } from "@/lib/db/schema";
|
||||
import type { PlantCardData } from "@/components/PlantCard";
|
||||
|
||||
export type { PlantCardData };
|
||||
|
||||
/**
|
||||
* Get all plants with their disease counts for the browse page.
|
||||
*/
|
||||
export async function getBrowsePlants(): Promise<PlantCardData[]> {
|
||||
const db = getDb();
|
||||
|
||||
// LEFT JOIN to include plants with zero diseases
|
||||
const rows = await db
|
||||
.select({
|
||||
id: plants.id,
|
||||
commonName: plants.commonName,
|
||||
scientificName: plants.scientificName,
|
||||
family: plants.family,
|
||||
category: plants.category,
|
||||
diseaseCount: sql<number>`COUNT(${diseases.id})`,
|
||||
})
|
||||
.from(plants)
|
||||
.leftJoin(diseases, eq(diseases.plantId, plants.id))
|
||||
.groupBy(plants.id)
|
||||
.orderBy(plants.commonName);
|
||||
|
||||
return rows.map((r) => ({
|
||||
id: r.id,
|
||||
commonName: r.commonName,
|
||||
scientificName: r.scientificName,
|
||||
family: r.family,
|
||||
category: r.category,
|
||||
diseaseCount: r.diseaseCount,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single plant with disease count (for detail page lookups).
|
||||
*/
|
||||
export async function getBrowsePlant(id: string): Promise<PlantCardData | null> {
|
||||
const db = getDb();
|
||||
const rows = await db
|
||||
.select({
|
||||
id: plants.id,
|
||||
commonName: plants.commonName,
|
||||
scientificName: plants.scientificName,
|
||||
family: plants.family,
|
||||
category: plants.category,
|
||||
diseaseCount: sql<number>`COUNT(${diseases.id})`,
|
||||
})
|
||||
.from(plants)
|
||||
.leftJoin(diseases, eq(diseases.plantId, plants.id))
|
||||
.where(eq(plants.id, id))
|
||||
.groupBy(plants.id)
|
||||
.limit(1);
|
||||
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get featured plants for the homepage (subset).
|
||||
*/
|
||||
const FEATURED_IDS = [
|
||||
"tomato",
|
||||
"basil",
|
||||
"rose",
|
||||
"monstera",
|
||||
"snake-plant",
|
||||
"pepper",
|
||||
"apple",
|
||||
"corn",
|
||||
"wheat",
|
||||
"strawberry",
|
||||
"blueberry",
|
||||
"lettuce",
|
||||
];
|
||||
|
||||
export async function getFeaturedPlants(): Promise<PlantCardData[]> {
|
||||
const all = await getBrowsePlants();
|
||||
const featured = all.filter((p) => FEATURED_IDS.includes(p.id));
|
||||
// If fewer than expected are found, pad with first available plants
|
||||
if (featured.length < 6) {
|
||||
const rest = all.filter((p) => !FEATURED_IDS.includes(p.id));
|
||||
return [...featured, ...rest].slice(0, 12);
|
||||
}
|
||||
return featured.slice(0, 12);
|
||||
}
|
||||
Reference in New Issue
Block a user