diff --git a/apps/web-nextjs/src/app/api/v1/agents/[slug]/[id]/view/route.ts b/apps/web-nextjs/src/app/api/v1/agents/[slug]/[id]/view/route.ts index 18c7ce6a..8064763a 100644 --- a/apps/web-nextjs/src/app/api/v1/agents/[slug]/[id]/view/route.ts +++ b/apps/web-nextjs/src/app/api/v1/agents/[slug]/[id]/view/route.ts @@ -1,5 +1,4 @@ -import { agentStatsTable, db } from "@sudan-codex/db"; -import { eq, sql } from "drizzle-orm"; +import { updateViewCount } from "@sudan-codex/db"; import { NextRequest } from "next/server"; export async function GET( @@ -15,17 +14,12 @@ export async function GET( } try { - const newViewCount = await db - .update(agentStatsTable) - .set({ view_count: sql`${agentStatsTable.view_count} + 1` }) - .where(eq(agentStatsTable.agent_id, Number(id))) - .returning({ view_count: agentStatsTable.view_count }); - - return Response.json(newViewCount[0]); + const result = await updateViewCount("agents", Number(id)); + return Response.json(result); } catch (error) { - console.error(`Error fetching agent details for slug "${slug}":`, error); + console.error(`Error updating view count for agent slug "${slug}":`, error); return Response.json( - { error: "Failed to fetch agent details" }, + { error: "Failed to update view count" }, { status: 500 } ); } diff --git a/apps/web-nextjs/src/app/api/v1/companies/[slug]/[id]/view/route.ts b/apps/web-nextjs/src/app/api/v1/companies/[slug]/[id]/view/route.ts index d18ea120..57e25c58 100644 --- a/apps/web-nextjs/src/app/api/v1/companies/[slug]/[id]/view/route.ts +++ b/apps/web-nextjs/src/app/api/v1/companies/[slug]/[id]/view/route.ts @@ -1,5 +1,4 @@ -import { companyStatsTable, db } from "@sudan-codex/db"; -import { eq, sql } from "drizzle-orm"; +import { updateViewCount } from "@sudan-codex/db"; import { NextRequest } from "next/server"; export async function GET( @@ -15,17 +14,15 @@ export async function GET( } try { - const newViewCount = await db - .update(companyStatsTable) - .set({ view_count: sql`${companyStatsTable.view_count} + 1` }) - .where(eq(companyStatsTable.company_id, Number(id))) - .returning({ view_count: companyStatsTable.view_count }); - - return Response.json(newViewCount[0]); + const result = await updateViewCount("companies", Number(id)); + return Response.json(result); } catch (error) { - console.error(`Error fetching company details for slug "${slug}":`, error); + console.error( + `Error updating view count for company slug "${slug}":`, + error + ); return Response.json( - { error: "Failed to fetch company details" }, + { error: "Failed to update view count" }, { status: 500 } ); } diff --git a/apps/web-nextjs/src/app/api/v1/drugs/[slug]/[id]/view/route.ts b/apps/web-nextjs/src/app/api/v1/drugs/[slug]/[id]/view/route.ts index fef9c4a8..cad5f872 100644 --- a/apps/web-nextjs/src/app/api/v1/drugs/[slug]/[id]/view/route.ts +++ b/apps/web-nextjs/src/app/api/v1/drugs/[slug]/[id]/view/route.ts @@ -1,5 +1,4 @@ -import { db, drugStatsTable } from "@sudan-codex/db"; -import { eq, sql } from "drizzle-orm"; +import { updateViewCount } from "@sudan-codex/db"; import { NextRequest } from "next/server"; export async function GET( @@ -15,13 +14,8 @@ export async function GET( } try { - const newViewCount = await db - .update(drugStatsTable) - .set({ view_count: sql`${drugStatsTable.view_count} + 1` }) - .where(eq(drugStatsTable.drug_id, Number(id))) - .returning({ view_count: drugStatsTable.view_count }); - - return Response.json(newViewCount[0]); + const result = await updateViewCount("drugs", Number(id)); + return Response.json(result); } catch (error) { console.error(`Error updating view count for drug slug "${slug}":`, error); return Response.json( diff --git a/apps/web-nextjs/src/app/api/v1/generics/[slug]/[id]/view/route.ts b/apps/web-nextjs/src/app/api/v1/generics/[slug]/[id]/view/route.ts index 6c7dc70a..61488ef8 100644 --- a/apps/web-nextjs/src/app/api/v1/generics/[slug]/[id]/view/route.ts +++ b/apps/web-nextjs/src/app/api/v1/generics/[slug]/[id]/view/route.ts @@ -1,5 +1,4 @@ -import { db, genericStatsTable } from "@sudan-codex/db"; -import { eq, sql } from "drizzle-orm"; +import { updateViewCount } from "@sudan-codex/db"; import { NextRequest } from "next/server"; export async function GET( @@ -15,17 +14,15 @@ export async function GET( } try { - const newViewCount = await db - .update(genericStatsTable) - .set({ view_count: sql`${genericStatsTable.view_count} + 1` }) - .where(eq(genericStatsTable.generic_id, Number(id))) - .returning({ view_count: genericStatsTable.view_count }); - - return Response.json(newViewCount[0]); + const result = await updateViewCount("generics", Number(id)); + return Response.json(result); } catch (error) { - console.error(`Error fetching generic details for slug "${slug}":`, error); + console.error( + `Error updating view count for generic slug "${slug}":`, + error + ); return Response.json( - { error: "Failed to fetch generic details" }, + { error: "Failed to update view count" }, { status: 500 } ); } diff --git a/apps/web-nextjs/src/components/drugInfo/view-count.tsx b/apps/web-nextjs/src/components/drugInfo/view-count.tsx index c4448eb9..a31fc537 100644 --- a/apps/web-nextjs/src/components/drugInfo/view-count.tsx +++ b/apps/web-nextjs/src/components/drugInfo/view-count.tsx @@ -1,11 +1,13 @@ import "server-only"; +import { updateViewCount } from "@sudan-codex/db"; import { Suspense, use } from "react"; import { ErrorBoundary } from "react-error-boundary"; import type { GetDrugBySlugReturnType } from "@/services/server/getDrugs"; import { Skeleton } from "../ui/skeleton"; + type ViewCountProps = { id: number; createdAt: GetDrugBySlugReturnType["createdAt"]; @@ -19,8 +21,11 @@ const ViewCount = async ({ createdAt, updatedAt, entity, - slug, }: ViewCountProps) => { + const newView = await updateViewCount(entity, id).catch((error) => { + console.error(error); + return { view_count: 0 }; + }); return (

@@ -29,53 +34,22 @@ const ViewCount = async ({

Last updated: {updatedAt?.toISOString().split("T")[0]}

- Error loading view count

}> - }> - - -
+ +
); }; -const Count = ({ - id, - entity, - slug, -}: Pick) => { - const newView = use(fetchCount({ entity, id, slug })); - +const Count = async ({ + newView, +}: { + newView: { view_count: number | null }; +}) => { return (

- View count: {newView.view_count} + View count: {newView.view_count ?? 0}

); }; -export default ViewCount; -const fetchCount = async ({ - entity, - id, - slug, -}: { - entity: ViewCountProps["entity"]; - id: number; - slug: string; -}) => { - const baseUrl = process.env.NEXT_PUBLIC_SITE_URL; - const res = await fetch( - `${baseUrl}/api/v1/${entity}/${slug}/${id.toString()}/view`, - { - cache: "no-store", - } - ); - if (!res.ok) { - const error = await res.json(); - throw Error("view count error" + res.status + " " + error.message); - } - return (await res.json()) as { view_count: null | number }; -}; +export default ViewCount; diff --git a/packages/db/devv2.db-shm b/packages/db/devv2.db-shm index bda1c0d1..8611bb84 100644 Binary files a/packages/db/devv2.db-shm and b/packages/db/devv2.db-shm differ diff --git a/packages/db/devv2.db-wal b/packages/db/devv2.db-wal index 367d21a6..d8aca434 100644 Binary files a/packages/db/devv2.db-wal and b/packages/db/devv2.db-wal differ diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index 86f228e4..56f4bbec 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -3,6 +3,7 @@ export * from "./queries/agent"; export * from "./queries/company"; export * from "./queries/generic"; export * from "./queries/landingPage"; +export * from "./queries/view-count"; export * from "./schemas/agentsSchema"; export * from "./schemas/auth-schema"; export * from "./schemas/companySchema"; diff --git a/packages/db/src/queries/view-count.ts b/packages/db/src/queries/view-count.ts new file mode 100644 index 00000000..a5ad6c52 --- /dev/null +++ b/packages/db/src/queries/view-count.ts @@ -0,0 +1,43 @@ +import { eq, sql } from "drizzle-orm"; + +import { db } from "../db"; +import { agentStatsTable } from "../schemas/agentsSchema"; +import { companyStatsTable } from "../schemas/companySchema"; +import { drugStatsTable } from "../schemas/drugsSchema"; +import { genericStatsTable } from "../schemas/genericSchema"; + +export type EntityType = "agents" | "generics" | "companies" | "drugs"; + +export async function updateViewCount(entity: EntityType, id: number) { + if (!Number.isFinite(id)) { + throw new Error(`Invalid id: ${id}`); + } + const tables = { + agents: { table: agentStatsTable, column: agentStatsTable.agent_id }, + generics: { + table: genericStatsTable, + column: genericStatsTable.generic_id, + }, + companies: { + table: companyStatsTable, + column: companyStatsTable.company_id, + }, + drugs: { table: drugStatsTable, column: drugStatsTable.drug_id }, + }; + + const { table, column } = tables[entity]; + + const result = await db + .update(table) + .set({ + view_count: sql`${table.view_count} + 1`, + updatedAt: new Date(), + }) + .where(eq(column, id)) + .returning({ view_count: table.view_count }); + + if (!result[0]) { + throw new Error(`No stats row found for ${entity} with id ${id}`); + } + return result[0]; +}