diff --git a/apps/web/db/prepare.sql b/apps/web/db/prepare.sql index 62cba941..a4f9951d 100644 --- a/apps/web/db/prepare.sql +++ b/apps/web/db/prepare.sql @@ -16,6 +16,14 @@ CREATE TABLE `account` ( FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action ); --> statement-breakpoint +CREATE TABLE `contentToSpace` ( + `contentId` integer NOT NULL, + `spaceId` integer NOT NULL, + PRIMARY KEY(`contentId`, `spaceId`), + FOREIGN KEY (`contentId`) REFERENCES `storedContent`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`spaceId`) REFERENCES `space`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint CREATE TABLE `session` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `sessionToken` text(255) NOT NULL, @@ -24,10 +32,11 @@ CREATE TABLE `session` ( FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action ); --> statement-breakpoint -CREATE TABLE `spaces` ( +CREATE TABLE `space` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `name` text DEFAULT 'all' NOT NULL, - `description` text(255) + `user` text(255), + FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action ); --> statement-breakpoint CREATE TABLE `storedContent` ( @@ -36,12 +45,10 @@ CREATE TABLE `storedContent` ( `title` text(255), `description` text(255), `url` text NOT NULL, - `space` text(255) DEFAULT 'all', `savedAt` integer NOT NULL, `baseUrl` text(255), `image` text(255), `user` text(255), - FOREIGN KEY (`space`) REFERENCES `spaces`(`name`) ON UPDATE no action ON DELETE no action, FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action ); --> statement-breakpoint @@ -62,9 +69,9 @@ CREATE TABLE `verificationToken` ( --> statement-breakpoint CREATE INDEX `account_userId_idx` ON `account` (`userId`);--> statement-breakpoint CREATE INDEX `session_userId_idx` ON `session` (`userId`);--> statement-breakpoint -CREATE INDEX `spaces_name_idx` ON `spaces` (`name`);--> statement-breakpoint +CREATE INDEX `spaces_name_idx` ON `space` (`name`);--> statement-breakpoint +CREATE INDEX `spaces_user_idx` ON `space` (`user`);--> statement-breakpoint CREATE INDEX `storedContent_url_idx` ON `storedContent` (`url`);--> statement-breakpoint CREATE INDEX `storedContent_savedAt_idx` ON `storedContent` (`savedAt`);--> statement-breakpoint CREATE INDEX `storedContent_title_idx` ON `storedContent` (`title`);--> statement-breakpoint -CREATE INDEX `storedContent_space_idx` ON `storedContent` (`space`);--> statement-breakpoint CREATE INDEX `storedContent_user_idx` ON `storedContent` (`user`); \ No newline at end of file diff --git a/apps/web/src/app/MessagePoster.tsx b/apps/web/src/app/MessagePoster.tsx index 3d0bbe7e..64dc89fd 100644 --- a/apps/web/src/app/MessagePoster.tsx +++ b/apps/web/src/app/MessagePoster.tsx @@ -8,7 +8,16 @@ function MessagePoster({ jwt }: { jwt: string }) { window.postMessage({ jwt }, '*'); }, [jwt]); - return null; + return ( + + ); } export default MessagePoster; diff --git a/apps/web/src/app/api/store/route.ts b/apps/web/src/app/api/store/route.ts index 3a4f7e27..06db08b9 100644 --- a/apps/web/src/app/api/store/route.ts +++ b/apps/web/src/app/api/store/route.ts @@ -1,6 +1,6 @@ import { db } from "@/server/db"; -import { eq } from "drizzle-orm"; -import { sessions, storedContent, users } from "@/server/db/schema"; +import { and, eq } from "drizzle-orm"; +import { contentToSpace, sessions, storedContent, users, space } from "@/server/db/schema"; import { type NextRequest, NextResponse } from "next/server"; import { env } from "@/env"; import { getMetaData } from "@/server/helpers"; @@ -31,6 +31,7 @@ export async function POST(req: NextRequest) { const data = await req.json() as { pageContent: string, url: string, + space?: string }; const metadata = await getMetaData(data.url); @@ -38,6 +39,12 @@ export async function POST(req: NextRequest) { let id: number | undefined = undefined; + let storeToSpace = data.space + + if (!storeToSpace) { + storeToSpace = 'all' + } + const storedContentId = await db.insert(storedContent).values({ content: data.pageContent, title: metadata.title, @@ -46,12 +53,33 @@ export async function POST(req: NextRequest) { baseUrl: metadata.baseUrl, image: metadata.image, savedAt: new Date(), - space: "all", user: session.user.id }) id = storedContentId.meta.last_row_id; + if (!id) { + return NextResponse.json({ message: "Error", error: "Error in CF function" }, { status: 500 }); + } + + let spaceID = 0; + + const spaceData = await db.select().from(space).where(and(eq(space.name, storeToSpace), eq(space.user, session.user.id))).limit(1) + spaceID = spaceData[0]?.id + + if (!spaceData || spaceData.length === 0) { + const spaceId = await db.insert(space).values({ + name: storeToSpace, + user: session.user.id + }) + spaceID = spaceId.meta.last_row_id; + } + + await db.insert(contentToSpace).values({ + contentId: id as number, + spaceId: spaceID + }) + const res = await Promise.race([ fetch("https://cf-ai-backend.dhravya.workers.dev/add", { method: "POST", diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index d1d47ae5..221ce2b4 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,5 +1,12 @@ import { db } from '@/server/db'; -import { sessions, storedContent, users } from '@/server/db/schema'; +import { + contentToSpace, + sessions, + space, + StoredContent, + storedContent, + users, +} from '@/server/db/schema'; import { eq, inArray } from 'drizzle-orm'; import { cookies, headers } from 'next/headers'; import { redirect } from 'next/navigation'; @@ -47,12 +54,15 @@ export default async function Home() { return redirect('/api/auth/signin'); } - const posts = await db + // Fetch all content for the user + const contents = await db .select() .from(storedContent) - .where(eq(storedContent.user, userData.id)); + .where(eq(storedContent.user, userData.id)) + .all(); - const collectedSpaces = transformContent(posts); + const collectedSpaces = + contents.length > 0 ? await transformContent(contents) : []; return (
diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 1af37025..66ca1652 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -26,8 +26,7 @@ export default function Sidebar() { description: '', image: 'https://code.visualstudio.com/favicon.ico', baseUrl: 'https://code.visualstudio.com', - savedAt: new Date(), - space: 'Development', + savedAt: new Date() }, { id: 1, @@ -37,8 +36,7 @@ export default function Sidebar() { description: '', image: 'https://github.com/favicon.ico', baseUrl: 'https://github.com', - savedAt: new Date(), - space: 'Development', + savedAt: new Date() }, ]; diff --git a/apps/web/src/components/Sidebar/CategoryItem.tsx b/apps/web/src/components/Sidebar/CategoryItem.tsx new file mode 100644 index 00000000..0cf8a70c --- /dev/null +++ b/apps/web/src/components/Sidebar/CategoryItem.tsx @@ -0,0 +1,298 @@ +'use client'; +import { cleanUrl } from '@/lib/utils'; +import { StoredContent } from '@/server/db/schema'; +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, +} from '../ui/dropdown-menu'; +import { Label } from '../ui/label'; +import { + ArrowUpRight, + MoreHorizontal, + Tags, + ChevronDown, + Edit3, + Trash2, + Save, + ChevronRight, + Plus, + Minus, +} from 'lucide-react'; +import { useState } from 'react'; +import { + Drawer, + DrawerContent, + DrawerHeader, + DrawerTitle, + DrawerDescription, + DrawerFooter, + DrawerClose, +} from '../ui/drawer'; +import { Input } from '../ui/input'; +import { Textarea } from '../ui/textarea'; +import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; +import { + AnimatePresence, + motion, + Reorder, + useMotionValue, +} from 'framer-motion'; + +const pages: StoredContent[] = [ + { + id: 1, + content: '', + title: 'Visual Studio Code', + url: 'https://code.visualstudio.com', + description: '', + image: 'https://code.visualstudio.com/favicon.ico', + baseUrl: 'https://code.visualstudio.com', + savedAt: new Date(), + }, + { + id: 2, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, + { + id: 3, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, + { + id: 4, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, + { + id: 5, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, + { + id: 6, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, + { + id: 7, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, + { + id: 8, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, + { + id: 9, + content: '', + title: "yxshv/vscode: An unofficial remake of vscode's landing page", + url: 'https://github.com/yxshv/vscode', + description: '', + image: 'https://github.com/favicon.ico', + baseUrl: 'https://github.com', + savedAt: new Date(), + }, +]; +export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => { + const [isExpanded, setIsExpanded] = useState(false); + const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false); + + const [items, setItems] = useState(pages); + + return ( + <> +
+ + + + + + Edit Page Details + + Change the page details + + + {cleanUrl(item.url)} + + + +
+ + +
+
+ +