diff --git a/app/api/renders/in/route.ts b/app/api/renders/in/route.ts index c62f392..2139e16 100644 --- a/app/api/renders/in/route.ts +++ b/app/api/renders/in/route.ts @@ -8,15 +8,32 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: "Not authenticated" }, { status: 401 }); } try { + const pageParam = request.nextUrl.searchParams.get("page"); + const pageSizeParam = request.nextUrl.searchParams.get("pageSize"); + const page = Math.max(1, Number(pageParam || 1)); + const pageSize = Math.max(1, Math.min(50, Number(pageSizeParam || 5))); + const offset = (page - 1) * pageSize; + const pool = getPool(); + const countRes = await pool.query( + `SELECT COUNT(*)::int AS count FROM uploaded_files WHERE user_id = $1`, + [userId] + ); + const total = countRes.rows[0]?.count || 0; + const totalPages = Math.max(1, Math.ceil(total / pageSize)); + const result = await pool.query( `SELECT id, original_name, file_path, upload_status, created_at FROM uploaded_files WHERE user_id = $1 - ORDER BY created_at DESC`, - [userId] + ORDER BY created_at DESC + LIMIT $2 OFFSET $3`, + [userId, pageSize, offset] ); - return NextResponse.json({ files: result.rows }); + return NextResponse.json({ + files: result.rows, + pagination: { page, pageSize, total, totalPages }, + }); } catch (err: any) { return NextResponse.json( { error: err.message || "Server error" }, diff --git a/app/contact/page.tsx b/app/contact/page.tsx new file mode 100644 index 0000000..3bc7460 --- /dev/null +++ b/app/contact/page.tsx @@ -0,0 +1,72 @@ +"use client"; + +import Link from "next/link"; +import ContactForm from "@/components/ContactForm"; + +export default function ContactPage() { + return ( +
+
+ {/* Header */} +
+ + + + + + Back To Home + +

+ Contact Us +

+

+ We look forward to hearing from you and helping you find the perfect + cloud solution for your needs. Our team of experts is ready to + assist you with any questions or concerns you may have. +

+
+ + {/* Contact Form Component */} +
+ +
+ + {/* Call to Action */} +
+

+ Looking to start a project with us? +

+
+ + Create an account + + or + + Schedule a consultation + +
+
+
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index 564052f..4fe3ad1 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -166,8 +166,15 @@ export default function Home() { { + e.preventDefault(); + const pricingSection = document.getElementById("pricing"); + if (pricingSection) { + pricingSection.scrollIntoView({ behavior: "smooth" }); + } + }} + className="px-4 py-2 text-gray-700 hover:text-gray-900 cursor-pointer" > Pricing @@ -270,12 +277,18 @@ export default function Home() { power so you can focus on the creative side of your work.

- - +
@@ -318,7 +331,7 @@ export default function Home() { )} {/* Pricing Section */} -
+

Available Plans diff --git a/app/renders/page.tsx b/app/renders/page.tsx index 9922f52..60503ed 100644 --- a/app/renders/page.tsx +++ b/app/renders/page.tsx @@ -3,6 +3,14 @@ import { useEffect, useState } from "react"; import { useAuth } from "@/lib/auth-context"; import { useRouter } from "next/navigation"; +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationPrevious, + PaginationNext, +} from "@/components/ui/pagination"; interface UploadedFile { id: string; @@ -19,6 +27,9 @@ export default function RendersPage() { const [loading, setLoading] = useState(true); const [authInitialized, setAuthInitialized] = useState(false); const [deletingId, setDeletingId] = useState(null); + const [page, setPage] = useState(1); + const pageSize = 5; + const [totalPages, setTotalPages] = useState(1); useEffect(() => { const timer = setTimeout(() => setAuthInitialized(true), 100); @@ -31,19 +42,25 @@ export default function RendersPage() { router.push("/login?redirect=/renders"); return; } - fetchFiles(); + fetchFiles(page); // eslint-disable-next-line - }, [authInitialized, isAuthenticated, user]); + }, [authInitialized, isAuthenticated, user, page]); - async function fetchFiles() { + async function fetchFiles(pageNum: number) { + setLoading(true); try { - const res = await fetch("/api/renders/in", { - headers: { "user-id": user?.id }, - }); + const res = await fetch( + `/api/renders/in?page=${pageNum}&pageSize=${pageSize}`, + { + headers: { "user-id": user?.id }, + } + ); const data = await res.json(); setFiles(data.files || []); + setTotalPages(data.pagination?.totalPages || 1); } catch { setFiles([]); + setTotalPages(1); } finally { setLoading(false); } @@ -65,7 +82,7 @@ export default function RendersPage() { }); const data = await res.json(); if (!res.ok) throw new Error(data?.error || "Delete failed"); - await fetchFiles(); + await fetchFiles(page); } catch (e) { alert((e as any).message || "Delete failed"); } finally { @@ -81,6 +98,9 @@ export default function RendersPage() { ); } + const canPrev = page > 1; + const canNext = page < totalPages; + return (
@@ -95,62 +115,112 @@ export default function RendersPage() { ) : files.length === 0 ? (
No files uploaded yet.
) : ( - - - - - - - - - - - - {files.map((f) => ( - - - - - - + <> +
FilenameStatusUploaded AtDownloadActions
{f.original_name} - - {f.upload_status} - - - {new Date(f.created_at).toLocaleString()} - - - Download - - - -
+ + + + + + + - ))} - -
FilenameStatusUploaded AtDownloadActions
+ + + {files.map((f) => ( + + {f.original_name} + + + {f.upload_status} + + + + {new Date(f.created_at).toLocaleString()} + + + + Download + + + + + + + ))} + + +
+ + + + { + e.preventDefault(); + if (canPrev) setPage(page - 1); + }} + aria-disabled={!canPrev} + className={ + !canPrev ? "pointer-events-none opacity-50" : "" + } + /> + + {Array.from({ length: totalPages }, (_, i) => i + 1).map( + (p) => ( + + { + e.preventDefault(); + setPage(p); + }} + > + {p} + + + ) + )} + + { + e.preventDefault(); + if (canNext) setPage(page + 1); + }} + aria-disabled={!canNext} + className={ + !canNext ? "pointer-events-none opacity-50" : "" + } + /> + + + +
+ )} diff --git a/components/ContactForm.tsx b/components/ContactForm.tsx new file mode 100644 index 0000000..6f751bf --- /dev/null +++ b/components/ContactForm.tsx @@ -0,0 +1,115 @@ +"use client"; +import React from "react"; + +export default function ContactForm() { + return ( +
+ {/* Get in Touch Card */} +
+
+

Get in Touch

+
+
+ + + +
+

Phone Support

+

+1 (555) 123-4567

+

+ Available 24/7 for urgent matters +

+
+
+
+ + + + +
+
+

Sales Inquiries

+ + sales@reya.cloud + +
+
+

Technical Support

+ + support@reya.cloud + +
+
+

Report Abuse

+ + abuse@reya.cloud + +
+
+
+
+
+
+ + {/* Business Hours Card */} +
+
+

Business Hours

+
+
+

Sales Department

+

+ Monday - Friday: 9:00 AM - 6:00 PM EST +

+
+
+

Technical Support

+

24/7/365

+

+ Priority support for enterprise customers +

+
+
+

+ For urgent matters outside of business hours, please use our + 24/7 phone support or submit a ticket through your customer + portal. +

+
+
+
+
+
+ ); +} diff --git a/components/ui/pagination.tsx b/components/ui/pagination.tsx new file mode 100644 index 0000000..0d18541 --- /dev/null +++ b/components/ui/pagination.tsx @@ -0,0 +1,127 @@ +import * as React from "react" +import { + ChevronLeftIcon, + ChevronRightIcon, + MoreHorizontalIcon, +} from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button, buttonVariants } from "@/components/ui/button" + +function Pagination({ className, ...props }: React.ComponentProps<"nav">) { + return ( +