Skip to content

Commit

Permalink
add tab filter
Browse files Browse the repository at this point in the history
  • Loading branch information
idoubi committed Nov 19, 2023
1 parent b88c3a2 commit 6378015
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 40 deletions.
47 changes: 37 additions & 10 deletions web/app/api/gpts/all/route.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
import { getCount, getRandRows } from "@/app/models/gpts";
import {
getHotRows,
getLatestRows,
getRandRows,
getRecommendedRows,
getTotalCount,
} from "@/app/models/gpts";
import { respData, respErr } from "@/app/utils/resp";

export async function POST(req: Request) {
try {
if (req.body) {
const params = await req.json();
const { last_id, limit } = params;
const { last_id, limit, tab } = params;

const rows = await getRandRows(last_id, limit);
const count = await getCount();
const count = await getTotalCount();

return Response.json({
code: 0,
message: "ok",
data: {
if (tab === "latest") {
const rows = await getLatestRows(last_id, limit);
return respData({
rows: rows,
count: count,
},
});
}

if (tab === "recommended") {
const rows = await getRecommendedRows(last_id, limit);
return respData({
rows: rows,
count: count,
});
}

if (tab === "hot") {
const rows = await getHotRows(last_id, limit);
return respData({
rows: rows,
count: count,
});
}

const rows = await getRandRows(last_id, limit);
return respData({
rows: rows,
count: count,
});
}
} catch (e) {
console.log("get all gpts failed: ", e);
return Response.json({ code: -1, message: e });
return respErr("get gpts failed");
}
}
18 changes: 18 additions & 0 deletions web/app/components/GptsList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Gpts } from "@/app/types/gpts";
import { LazyLoadImage } from "react-lazy-load-image-component";
import Link from "next/link";
import moment from "moment";

interface Props {
gpts: Gpts[];
Expand Down Expand Up @@ -35,6 +36,23 @@ export default ({ gpts, loading }: Props) => {
<p className="mb-4 text-sm text-[#636262]">
{item.description}
</p>

<div className="flex items-center">
{item.rating &&
Array.from({ length: 5 }).map((_, idx: number) => (
<img
key={idx}
src="/star.svg"
alt=""
className="mr-1.5 inline-block w-4 flex-none"
/>
))}
<div className="flex-1"></div>

<p className="text-slate-500 text-sm">
{moment(item.created_at).fromNow()}
</p>
</div>
</div>
</Link>
);
Expand Down
55 changes: 55 additions & 0 deletions web/app/components/Tab/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Dispatch, SetStateAction } from "react";

import { Tab } from "@/app/types/tab";

interface Props {
tabValue: string;
setTabValue: Dispatch<SetStateAction<string>>;
}

export default ({ tabValue, setTabValue }: Props) => {
const tabs: Tab[] = [
{
name: "hot",
title: "Hot 🔥",
},
{
name: "recommended",
title: "Recommended",
},
{
name: "latest",
title: "Latest",
},
{
name: "random",
title: "Random",
},
];

return (
<section className="relative mt-4">
<div className="mx-auto max-w-7xl px-2 py-4 md:px-8 md:py-4 text-center">
<div
role="tablist"
className="tabs tabs-boxed tabs-sm md:tabs-md inline-block mx-auto"
>
{tabs.map((tab: Tab, idx: number) => {
return (
<a
role="tab"
key={idx}
className={`tab ${
tabValue === tab.name ? "bg-primary text-white" : ""
}`}
onClick={() => setTabValue(tab.name)}
>
{tab.title}
</a>
);
})}
</div>
</div>
</section>
);
};
64 changes: 50 additions & 14 deletions web/app/models/gpts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { QueryResultRow, sql } from "@vercel/postgres";
import { QueryResult, QueryResultRow, sql } from "@vercel/postgres";

import { Gpts } from "@/app/types/gpts";
import { isGptsSensitive } from "@/app/services/gpts";
Expand Down Expand Up @@ -73,23 +73,41 @@ export async function getRandRows(
): Promise<Gpts[]> {
const res =
await sql`SELECT * FROM gpts WHERE id > ${last_id} ORDER BY RANDOM() LIMIT ${limit}`;
if (res.rowCount === 0) {
return [];
}

const gpts: Gpts[] = [];
const { rows } = res;
rows.forEach((row) => {
const gpt = formatGpts(row);
if (gpt) {
gpts.push(gpt);
}
});
return getGptsFromSqlResult(res);
}

return gpts;
export async function getLatestRows(
last_id: number,
limit: number
): Promise<Gpts[]> {
const res =
await sql`SELECT * FROM gpts WHERE id > ${last_id} ORDER BY created_at DESC LIMIT ${limit}`;

return getGptsFromSqlResult(res);
}

export async function getRecommendedRows(
last_id: number,
limit: number
): Promise<Gpts[]> {
const res =
await sql`SELECT * FROM gpts WHERE is_recommended=true AND id > ${last_id} ORDER BY sort DESC LIMIT ${limit}`;

return getGptsFromSqlResult(res);
}

export async function getHotRows(
last_id: number,
limit: number
): Promise<Gpts[]> {
const res =
await sql`SELECT * FROM gpts WHERE rating IS NOT null AND id > ${last_id} ORDER BY rating DESC, sort DESC LIMIT ${limit}`;

return getGptsFromSqlResult(res);
}

export async function getCount(): Promise<number> {
export async function getTotalCount(): Promise<number> {
const res = await sql`SELECT count(1) as count FROM gpts LIMIT 1`;
if (res.rowCount === 0) {
return 0;
Expand All @@ -114,6 +132,23 @@ export async function findByUuid(uuid: string): Promise<Gpts | undefined> {
return gpts;
}

function getGptsFromSqlResult(res: QueryResult<QueryResultRow>): Gpts[] {
if (res.rowCount === 0) {
return [];
}

const gpts: Gpts[] = [];
const { rows } = res;
rows.forEach((row) => {
const gpt = formatGpts(row);
if (gpt) {
gpts.push(gpt);
}
});

return gpts;
}

function formatGpts(row: QueryResultRow): Gpts | undefined {
const gpts: Gpts = {
uuid: row.uuid,
Expand All @@ -127,6 +162,7 @@ function formatGpts(row: QueryResultRow): Gpts | undefined {
created_at: row.created_at,
updated_at: row.updated_at,
visit_url: "https://chat.openai.com/g/" + row.short_url,
rating: row.rating,
};

try {
Expand Down
12 changes: 8 additions & 4 deletions web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ import { Gpts } from "./types/gpts";
import GptsList from "./components/GptsList";
import ProductHunt from "./components/ProductHunt";
import Search from "./components/Search";
import Tab from "./components/Tab";

export default () => {
const [gpts, setGpts] = useState<Gpts[]>([]);
const [gptsCount, setGptsCount] = useState(0);
const [loading, setLoading] = useState(false);
const [tabValue, setTabValue] = useState("hot");

const fetchGpts = async () => {
const fetchGpts = async (tab: string) => {
const params = {
last_id: 0,
limit: 100,
limit: 50,
tab: tab,
};

setLoading(true);
Expand All @@ -39,14 +42,15 @@ export default () => {
};

useEffect(() => {
fetchGpts();
}, []);
fetchGpts(tabValue);
}, [tabValue]);

return (
<>
<Brand count={gptsCount} />
<ProductHunt />
<Search setGpts={setGpts} setLoading={setLoading} />
<Tab tabValue={tabValue} setTabValue={setTabValue} />
<GptsList gpts={gpts} loading={loading} />
</>
);
Expand Down
10 changes: 3 additions & 7 deletions web/app/services/gpts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,14 @@ export const searchGpts = async (question: string): Promise<Gpts[]> => {
};

export function isGptsSensitive(gpts: Gpts): boolean {
if (!gpts.name || !gpts.author_name || !gpts.description) {
return true;
}

const sensitiveKeywords = process.env.SENSITIVE_KEYWORDS || "";
const keywordsArr = sensitiveKeywords.split(",");
for (let i = 0, l = keywordsArr.length; i < l; i++) {
const keyword = keywordsArr[i].trim();
if (
gpts.name.includes(keyword) ||
gpts.description.includes(keyword) ||
gpts.author_name.includes(keyword)
(gpts.name && gpts.name.includes(keyword)) ||
(gpts.author_name && gpts.author_name.includes(keyword)) ||
(gpts.description && gpts.description.includes(keyword))
) {
console.log("gpt is sensitive: ", gpts.uuid, gpts.name, keyword);
return true;
Expand Down
1 change: 1 addition & 0 deletions web/app/types/gpts.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export interface Gpts {
updated_at: string;
detail?: any;
visit_url?: string;
rating?: number;
}
5 changes: 5 additions & 0 deletions web/app/types/tab.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface Tab {
name: string;
title: string;
icon?: JSX.Element;
}
24 changes: 24 additions & 0 deletions web/app/utils/resp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export function respData(data: any) {
return respJson(0, "ok", data);
}

export function respOk() {
return respJson(0, "ok");
}

export function respErr(message: string) {
return respJson(-1, message);
}

export function respJson(code: number, message: string, data?: any) {
let json = {
code: code,
message: message,
data: data,
};
if (data) {
json["data"] = data;
}

return Response.json(json);
}
File renamed without changes.
5 changes: 4 additions & 1 deletion data/install.sql → web/data/install.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ CREATE TABLE gpts (
created_at timestamptz,
updated_at timestamptz,
detail JSON,
index_updated_at INT NOT NULL DEFAULT 0
index_updated_at INT NOT NULL DEFAULT 0,
is_recommended BOOLEAN,
sort INTEGER NOT NULL DEFAULT 0,
rating SMALLINT
);
1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"@vercel/postgres": "^0.5.1",
"@zilliz/milvus2-sdk-node": "^2.3.3",
"daisyui": "^4.4.2",
"langchain": "^0.0.186",
"moment": "^2.29.4",
"next": "14.0.0",
Expand Down
Loading

0 comments on commit 6378015

Please sign in to comment.