Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions apps/web-nextjs/src/app/api/v1/agents/[slug]/[id]/view/route.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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 }
);
}
Expand Down
19 changes: 8 additions & 11 deletions apps/web-nextjs/src/app/api/v1/companies/[slug]/[id]/view/route.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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 }
);
}
Expand Down
12 changes: 3 additions & 9 deletions apps/web-nextjs/src/app/api/v1/drugs/[slug]/[id]/view/route.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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(
Expand Down
19 changes: 8 additions & 11 deletions apps/web-nextjs/src/app/api/v1/generics/[slug]/[id]/view/route.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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 }
);
}
Expand Down
56 changes: 15 additions & 41 deletions apps/web-nextjs/src/components/drugInfo/view-count.tsx
Original file line number Diff line number Diff line change
@@ -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";
Comment on lines 4 to 9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove unused imports left over from the previous implementation.

Suspense, use, ErrorBoundary, and Skeleton are no longer referenced after the refactor.

Proposed cleanup
-import { Suspense, use } from "react";
-import { ErrorBoundary } from "react-error-boundary";
 
 import type { GetDrugBySlugReturnType } from "@/services/server/getDrugs";
 
-import { Skeleton } from "../ui/skeleton";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Suspense, use } from "react";
import { ErrorBoundary } from "react-error-boundary";
import type { GetDrugBySlugReturnType } from "@/services/server/getDrugs";
import { Skeleton } from "../ui/skeleton";
import type { GetDrugBySlugReturnType } from "@/services/server/getDrugs";
🤖 Prompt for AI Agents
In `@apps/web-nextjs/src/components/drugInfo/view-count.tsx` around lines 4 - 9,
Remove the now-unused imports left from the prior implementation: delete the
named imports Suspense and use from "react", ErrorBoundary from
"react-error-boundary", and Skeleton from "../ui/skeleton" so only actually used
symbols remain in the import list; update the import statements that reference
these identifiers (e.g., the top-level imports containing Suspense/use and
ErrorBoundary/Skeleton) to only import what the current component uses, and run
a quick lint/TS check to ensure no unused-import errors remain.


type ViewCountProps = {
id: number;
createdAt: GetDrugBySlugReturnType["createdAt"];
Expand All @@ -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 (
<div className='flex items-center gap-2'>
<p className='text-muted-foreground text-sm'>
Expand All @@ -29,53 +34,22 @@ const ViewCount = async ({
<p className='text-muted-foreground text-sm'>
Last updated: {updatedAt?.toISOString().split("T")[0]}
</p>
<ErrorBoundary fallback={<p>Error loading view count</p>}>
<Suspense fallback={<Skeleton className='h-4 w-24' />}>
<Count
id={id}
entity={entity}
slug={slug}
/>
</Suspense>
</ErrorBoundary>

<Count newView={newView} />
</div>
);
};

const Count = ({
id,
entity,
slug,
}: Pick<ViewCountProps, "id" | "entity" | "slug">) => {
const newView = use(fetchCount({ entity, id, slug }));

const Count = async ({
newView,
}: {
newView: { view_count: number | null };
}) => {
return (
<p className='text-muted-foreground text-sm'>
View count: {newView.view_count}
View count: {newView.view_count ?? 0}
</p>
);
};
Comment on lines +43 to 53

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Count doesn't need to be async.

The component receives data via props and performs no async work. Dropping async avoids an unnecessary microtask/promise wrapper.

Proposed fix
-const Count = async ({
+const Count = ({
   newView,
 }: {
   newView: { view_count: number | null };
 }) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const Count = async ({
newView,
}: {
newView: { view_count: number | null };
}) => {
return (
<p className='text-muted-foreground text-sm'>
View count: {newView.view_count}
View count: {newView.view_count ?? 0}
</p>
);
};
const Count = ({
newView,
}: {
newView: { view_count: number | null };
}) => {
return (
<p className='text-muted-foreground text-sm'>
View count: {newView.view_count ?? 0}
</p>
);
};
🤖 Prompt for AI Agents
In `@apps/web-nextjs/src/components/drugInfo/view-count.tsx` around lines 43 - 53,
The Count component is declared async but performs no asynchronous work; remove
the async keyword from the Count function declaration (the Count component that
takes newView: { view_count: number | null }) so it returns JSX synchronously
and avoids creating an unnecessary Promise/microtask wrapper; update the Count
declaration accordingly and leave the JSX return (View count:
{newView.view_count ?? 0}) unchanged.

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;
Binary file modified packages/db/devv2.db-shm
Binary file not shown.
Binary file modified packages/db/devv2.db-wal
Binary file not shown.
1 change: 1 addition & 0 deletions packages/db/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
43 changes: 43 additions & 0 deletions packages/db/src/queries/view-count.ts
Original file line number Diff line number Diff line change
@@ -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];
}
Loading