Add document page and enhance upload functionality with user-specific document handling
This commit is contained in:
parent
527ae45471
commit
e8060b0719
117
app/dashboard/documents/[id]/page.tsx
Normal file
117
app/dashboard/documents/[id]/page.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { AppSidebar } from "@/components/app-sidebar";
|
||||||
|
import { NavActions } from "@/components/nav-actions";
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
} from "@/components/ui/breadcrumb";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import {
|
||||||
|
SidebarInset,
|
||||||
|
SidebarProvider,
|
||||||
|
SidebarTrigger,
|
||||||
|
} from "@/components/ui/sidebar";
|
||||||
|
import { createClient } from "@/utils/supabase/server";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import { remark } from "remark";
|
||||||
|
import remarkHtml from "remark-html";
|
||||||
|
|
||||||
|
export default async function DocumentPage({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: { id: string };
|
||||||
|
}) {
|
||||||
|
const supabase = await createClient();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: { user },
|
||||||
|
} = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return redirect("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the document details based on the ID from params
|
||||||
|
const { data: document, error } = await supabase
|
||||||
|
.from("documents")
|
||||||
|
.select("*")
|
||||||
|
.eq("id", params.id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error || !document) {
|
||||||
|
console.error("Error fetching document:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the document doesn't exist, redirect to the documents page or handle it accordingly
|
||||||
|
if (!document) {
|
||||||
|
return redirect("/dashboard");
|
||||||
|
}
|
||||||
|
const { data: documents, error: documentsError } = await supabase
|
||||||
|
.from("documents")
|
||||||
|
.select("id, file_name, created_at, owner")
|
||||||
|
.eq("owner", user.id)
|
||||||
|
.order("created_at", { ascending: false });
|
||||||
|
|
||||||
|
if (documentsError) {
|
||||||
|
console.error("Error fetching documents:", error);
|
||||||
|
return <div>Error loading documents.</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages = (document.ocr_data as any).pages.map(
|
||||||
|
(page: any) => page.markdown
|
||||||
|
);
|
||||||
|
|
||||||
|
const processedContent = await remark()
|
||||||
|
.use(remarkHtml)
|
||||||
|
.process(pages.join(" "));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarProvider>
|
||||||
|
<AppSidebar
|
||||||
|
documents={documents.map((d) => {
|
||||||
|
return {
|
||||||
|
name: d.file_name,
|
||||||
|
url: `/dashboard/documents/${d.id}`,
|
||||||
|
emoji: "📄",
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<SidebarInset>
|
||||||
|
<header className="flex h-14 shrink-0 items-center gap-2">
|
||||||
|
<div className="flex flex-1 items-center gap-2 px-3">
|
||||||
|
<SidebarTrigger />
|
||||||
|
<Separator
|
||||||
|
orientation="vertical"
|
||||||
|
className="mr-2 data-[orientation=vertical]:h-4"
|
||||||
|
/>
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage className="line-clamp-1">
|
||||||
|
{document.file_name || "Document Details"}
|
||||||
|
</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
</div>
|
||||||
|
<div className="ml-auto px-3">
|
||||||
|
<NavActions />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div
|
||||||
|
className="prose mx-auto px-4 py-10
|
||||||
|
text-white
|
||||||
|
prose-h1:font-semibold prose-h1:text-2xl prose-h1:mb-4 prose-h1:text-white
|
||||||
|
prose-h2:font-medium prose-h2:text-xl prose-h2:mb-3 prose-h2:text-white
|
||||||
|
prose-a:text-blue-400 hover:prose-a:underline
|
||||||
|
prose-p:leading-7 prose-p:text-gray-200
|
||||||
|
prose-blockquote:italic prose-blockquote:border-l-4 prose-blockquote:pl-4 prose-blockquote:border-gray-600 prose-blockquote:text-gray-300
|
||||||
|
prose-code:bg-gray-800 prose-code:rounded prose-code:px-1 prose-code:py-0.5 prose-code:text-gray-200
|
||||||
|
prose-img:rounded-lg prose-img:shadow-sm"
|
||||||
|
dangerouslySetInnerHTML={{ __html: String(processedContent) }}
|
||||||
|
></div>
|
||||||
|
</SidebarInset>
|
||||||
|
</SidebarProvider>
|
||||||
|
);
|
||||||
|
}
|
@ -28,9 +28,28 @@ export default async function Page() {
|
|||||||
return redirect("/login");
|
return redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { data: documents, error } = await supabase
|
||||||
|
.from("documents")
|
||||||
|
.select("id, file_name, created_at, owner")
|
||||||
|
.eq("owner", user.id)
|
||||||
|
.order("created_at", { ascending: false });
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error("Error fetching documents:", error);
|
||||||
|
return <div>Error loading documents.</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<AppSidebar />
|
<AppSidebar
|
||||||
|
documents={documents.map((d) => {
|
||||||
|
return {
|
||||||
|
name: d.file_name,
|
||||||
|
url: `/dashboard/documents/${d.id}`,
|
||||||
|
emoji: "📄",
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<header className="flex h-14 shrink-0 items-center gap-2">
|
<header className="flex h-14 shrink-0 items-center gap-2">
|
||||||
<div className="flex flex-1 items-center gap-2 px-3">
|
<div className="flex flex-1 items-center gap-2 px-3">
|
||||||
@ -50,7 +69,7 @@ export default async function Page() {
|
|||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<UploadZone />
|
<UploadZone user={user} />
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
</SidebarProvider>
|
</SidebarProvider>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { createClient } from "@/utils/supabase/server";
|
import { createClient } from "@/utils/supabase/server";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { Mistral } from "@mistralai/mistralai";
|
import { Mistral } from "@mistralai/mistralai";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
const apiKey = process.env.MISTRAL_API_KEY;
|
const apiKey = process.env.MISTRAL_API_KEY;
|
||||||
const client = new Mistral({ apiKey: apiKey });
|
const client = new Mistral({ apiKey: apiKey });
|
||||||
@ -42,6 +43,5 @@ export async function POST(request: Request) {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||||
}
|
}
|
||||||
console.log("Document updated successfully:", data);
|
return redirect(`/dashboard/documents/${id}`); // Redirect to the document page after processing
|
||||||
return NextResponse.json({ message: "File processed successfully" });
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "tw-animate-css";
|
@import "tw-animate-css";
|
||||||
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
@ -2,19 +2,15 @@
|
|||||||
import { createClient } from "@/utils/supabase/client";
|
import { createClient } from "@/utils/supabase/client";
|
||||||
import { CloudUpload } from "lucide-react";
|
import { CloudUpload } from "lucide-react";
|
||||||
|
|
||||||
export default async function UploadZone() {
|
export default function UploadZone({ user }: { user?: { id: string } }) {
|
||||||
const supabase = await createClient();
|
const supabase = createClient();
|
||||||
|
|
||||||
const {
|
|
||||||
data: { user },
|
|
||||||
} = await supabase.auth.getUser();
|
|
||||||
|
|
||||||
const onUpload = async (file: File) => {
|
const onUpload = async (file: File) => {
|
||||||
const uuid = crypto.randomUUID();
|
const uuid = crypto.randomUUID();
|
||||||
|
|
||||||
const { data: fileData, error: fileError } = await supabase.storage
|
const { data: fileData, error: fileError } = await supabase.storage
|
||||||
.from("documents")
|
.from("documents")
|
||||||
.upload(`public/${uuid}.pdf`, file);
|
.upload(`${user!.id}/${uuid}.pdf`, file);
|
||||||
|
|
||||||
if (fileError) {
|
if (fileError) {
|
||||||
console.error(fileError);
|
console.error(fileError);
|
||||||
|
@ -27,8 +27,14 @@ import {
|
|||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarRail,
|
SidebarRail,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
import { createClient } from "@/utils/supabase/client";
|
||||||
|
|
||||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
export function AppSidebar({
|
||||||
|
documents,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof Sidebar> & {
|
||||||
|
documents?: Array<{ name: string; url: string; emoji?: string }>;
|
||||||
|
}) {
|
||||||
const data = {
|
const data = {
|
||||||
navMain: [
|
navMain: [
|
||||||
{
|
{
|
||||||
@ -65,59 +71,8 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|||||||
icon: MessageCircleQuestion,
|
icon: MessageCircleQuestion,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
favorites: [
|
|
||||||
{
|
|
||||||
name: "Project Management & Task Tracking",
|
|
||||||
url: "#",
|
|
||||||
emoji: "📊",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Family Recipe Collection & Meal Planning",
|
|
||||||
url: "#",
|
|
||||||
emoji: "🍳",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Fitness Tracker & Workout Routines",
|
|
||||||
url: "#",
|
|
||||||
emoji: "💪",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Book Notes & Reading List",
|
|
||||||
url: "#",
|
|
||||||
emoji: "📚",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Sustainable Gardening Tips & Plant Care",
|
|
||||||
url: "#",
|
|
||||||
emoji: "🌱",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Language Learning Progress & Resources",
|
|
||||||
url: "#",
|
|
||||||
emoji: "🗣️",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Home Renovation Ideas & Budget Tracker",
|
|
||||||
url: "#",
|
|
||||||
emoji: "🏠",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Personal Finance & Investment Portfolio",
|
|
||||||
url: "#",
|
|
||||||
emoji: "💰",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Movie & TV Show Watchlist with Reviews",
|
|
||||||
url: "#",
|
|
||||||
emoji: "🎬",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Daily Habit Tracker & Goal Setting",
|
|
||||||
url: "#",
|
|
||||||
emoji: "✅",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sidebar className="border-r-0" {...props}>
|
<Sidebar className="border-r-0" {...props}>
|
||||||
<SidebarHeader>
|
<SidebarHeader>
|
||||||
@ -130,7 +85,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
|||||||
<NavMain items={data.navMain} />
|
<NavMain items={data.navMain} />
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
<SidebarContent>
|
<SidebarContent>
|
||||||
<NavDocuments documents={data.favorites} />
|
<NavDocuments documents={documents} />
|
||||||
<NavSecondary items={data.navSecondary} className="mt-auto" />
|
<NavSecondary items={data.navSecondary} className="mt-auto" />
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarRail />
|
<SidebarRail />
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"@supabase/ssr": "latest",
|
"@supabase/ssr": "latest",
|
||||||
"@supabase/supabase-js": "latest",
|
"@supabase/supabase-js": "latest",
|
||||||
"@tailwindcss/postcss": "^4.1.0",
|
"@tailwindcss/postcss": "^4.1.0",
|
||||||
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
"ai": "^4.2.11",
|
"ai": "^4.2.11",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.20",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@ -31,6 +32,8 @@
|
|||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
|
"remark": "^15.0.1",
|
||||||
|
"remark-html": "^16.0.1",
|
||||||
"tw-animate-css": "^1.2.5",
|
"tw-animate-css": "^1.2.5",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user