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)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+ Cancel
+
+
+
+ Delete
+
+
+
+
+
+
+ {isExpanded && (
+
+
+ {items.map((item, i) => (
+
+ setItems((prev) => prev.filter((_, index) => i !== index))
+ }
+ />
+ ))}
+
+
+ )}
+
+ >
+ );
+};
+
+export const CategoryPage: React.FC<{
+ item: StoredContent;
+ index: number;
+ onRemove?: () => void;
+}> = ({ item, onRemove, index }) => {
+ return (
+
+
+
+

+
+
+
+
+ {item.title ?? 'Untitled website'}
+
+
+
+
+ );
+};
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
index b8c1fbb8..52bab0f9 100644
--- a/apps/web/src/components/Sidebar/index.tsx
+++ b/apps/web/src/components/Sidebar/index.tsx
@@ -28,10 +28,13 @@ const menuItemsBottom: Array