summaryrefslogtreecommitdiff
path: root/app/src/pages/categorize.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/pages/categorize.tsx')
-rw-r--r--app/src/pages/categorize.tsx171
1 files changed, 171 insertions, 0 deletions
diff --git a/app/src/pages/categorize.tsx b/app/src/pages/categorize.tsx
new file mode 100644
index 0000000..d91d8c1
--- /dev/null
+++ b/app/src/pages/categorize.tsx
@@ -0,0 +1,171 @@
+import * as Bun from "bun";
+import { Suspense } from "react";
+import { TwitterApiService, TwitterBookmark } from "../lib/twitter-api";
+import {
+ userCategories,
+ type CategorizationResponse,
+} from "../lib/categorization";
+import { LLMService } from "../lib/llm-service";
+import ProcessedBookmark from "../components/cat/Entry";
+
+interface CategorizePageProps {
+ bookmarks: Awaited<ReturnType<TwitterApiService["fetchAllBookmarks"]>>;
+ currentBookmarkIndex: number;
+ categorization?: CategorizationResponse;
+ error?: string;
+}
+
+async function CategorizationFetcher({
+ bookmarks,
+ currentIndex,
+}: {
+ bookmarks: TwitterBookmark[];
+ currentIndex: number;
+}) {
+ if (currentIndex >= bookmarks.length) {
+ return (
+ <div className="text-center py-12">
+ <h2 className="text-2xl font-bold text-gray-900 mb-4">
+ All Bookmarks Categorized!
+ </h2>
+ <p className="text-gray-600 mb-6">
+ You've successfully categorized all your bookmarks.
+ </p>
+ <a
+ href="/"
+ className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
+ >
+ Back to Bookmarks
+ </a>
+ </div>
+ );
+ }
+
+ const bookmark = bookmarks[currentIndex];
+ const llmRes = await callLLM(bookmark!);
+ if ("error" in llmRes)
+ return (
+ <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
+ Error categorizing bookmark: {llmRes.error}
+ </div>
+ );
+ return (
+ <div className="">
+ <ProcessedBookmark
+ bookmark={bookmark!}
+ categorization={llmRes.ok}
+ currentIndex={currentIndex}
+ totalCount={bookmarks.length}
+ />
+ </div>
+ );
+}
+
+function LoadingSpinner() {
+ return (
+ <div className="flex items-center justify-center py-12">
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
+ <span className="ml-3 text-gray-600">
+ Loading your Twitter bookmarks...
+ </span>
+ </div>
+ );
+}
+
+export default async function CategorizePage(props: any) {
+ // console.log({ props });
+ const params = new URLSearchParams(props.query);
+ const currentIndex = Number(params.get("idx") || "0");
+ const cookie = Bun.env.TWATTER_COKI;
+
+ if (!cookie) {
+ return (
+ <div className="text-red-600 text-center py-12">
+ Missing Twitter cookie configuration
+ </div>
+ );
+ }
+ const twbookmarks = await getTwData(cookie);
+ if ("error" in twbookmarks) {
+ return (
+ <div className="text-red-600 text-center py-12">
+ Error fetching Twatter bookmarks
+ </div>
+ );
+ }
+ // const currentIndex = parseInt(searchParams.index || '0', 10);
+ const totalCount = twbookmarks.ok.length;
+
+ return (
+ <div className="min-h-screen bg-gray-50 py-8">
+ <div className="max-w-4xl mx-auto px-4">
+ <title>Categorize Bookmarks - SORMARK</title>
+
+ <div className="mb-8">
+ <h1 className="text-4xl font-bold tracking-tight mb-4">
+ Categorize Bookmarks
+ </h1>
+ <p className="text-lg text-gray-600">
+ Review and categorize your Twitter bookmarks one by one
+ </p>
+ </div>
+
+ <Suspense fallback={<LoadingSpinner />}>
+ <>
+ <div className="bg-blue-600 text-white p-4">
+ <p className="text-blue-100 mt-1">
+ Bookmark {currentIndex + 1} of {totalCount}
+ </p>
+ <div className="w-full bg-blue-800 rounded-full h-2 mt-2">
+ <div
+ className="bg-white h-2 rounded-full transition-all duration-300"
+ style={{
+ width: `${((currentIndex + 1) / totalCount) * 100}%`,
+ }}
+ ></div>
+ </div>
+ </div>
+ <CategorizationFetcher
+ bookmarks={twbookmarks.ok}
+ currentIndex={currentIndex}
+ />
+ </>
+ </Suspense>
+ </div>
+ </div>
+ );
+}
+
+import bmarks from "../lib/testData.json";
+async function getTwData(cookie: string) {
+ try {
+ // const twitterService = new TwitterApiService(cookie);
+ // const bookmarks = await twitterService.fetchAllBookmarks();
+ // return { ok: bookmarks };
+ return { ok: bmarks } as any;
+ } catch (error) {
+ return { error: `${error}` };
+ }
+}
+
+async function callLLM(bookmark: TwitterBookmark) {
+ const apiKey = Bun.env.GEMINI_API_KEY!;
+ try {
+ const llmService = new LLMService(apiKey);
+
+ const categorization = await llmService.categorizeBookmark({
+ bookmark,
+ userCategories,
+ });
+
+ return { ok: categorization };
+ } catch (error) {
+ return { error: `${error}` };
+ }
+}
+
+export const getConfig = async () => {
+ return {
+ render: "dynamic",
+ } as const;
+};