Compare commits
	
		
			No commits in common. "b368473216d7dd9f08770443861b135e2b6b798f" and "bb89a84ba85cf43030b4ae17e88709ed6c352fb4" have entirely different histories.
		
	
	
		
			b368473216
			...
			bb89a84ba8
		
	
		
| @ -15,6 +15,17 @@ datasource db { | |||||||
|     url      = env("DATABASE_URL") |     url      = env("DATABASE_URL") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | model Post { | ||||||
|  |     id        Int      @id @default(autoincrement()) | ||||||
|  |     name      String | ||||||
|  |     createdAt DateTime @default(now()) | ||||||
|  |     updatedAt DateTime @updatedAt | ||||||
|  | 
 | ||||||
|  |     createdBy   User   @relation(fields: [createdById], references: [id]) | ||||||
|  |     createdById String | ||||||
|  | 
 | ||||||
|  |     @@index([name]) | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // Necessary for Next auth | // Necessary for Next auth | ||||||
| model Account { | model Account { | ||||||
| @ -52,6 +63,7 @@ model User { | |||||||
|     image         String? |     image         String? | ||||||
|     accounts      Account[] |     accounts      Account[] | ||||||
|     sessions      Session[] |     sessions      Session[] | ||||||
|  |     posts         Post[] | ||||||
|     files         File[]    // Relation to the File model |     files         File[]    // Relation to the File model | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -73,5 +85,4 @@ model File { | |||||||
|     description String @default("") |     description String @default("") | ||||||
|     uploadedBy User?    @relation(fields: [uploadedById], references: [id], onDelete: SetNull) |     uploadedBy User?    @relation(fields: [uploadedById], references: [id], onDelete: SetNull) | ||||||
|     uploadedById String? |     uploadedById String? | ||||||
|     public     Boolean  @default(false) // Indicates if the file is public or private |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1 +0,0 @@ | |||||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" /></svg> |  | ||||||
| Before Width: | Height: | Size: 314 B | 
| @ -1 +0,0 @@ | |||||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17.9,17.39C17.64,16.59 16.89,16 16,16H15V13A1,1 0 0,0 14,12H8V10H10A1,1 0 0,0 11,9V7H13A2,2 0 0,0 15,5V4.59C17.93,5.77 20,8.64 20,12C20,14.08 19.2,15.97 17.9,17.39M11,19.93C7.05,19.44 4,16.08 4,12C4,11.38 4.08,10.78 4.21,10.21L9,15V16A2,2 0 0,0 11,18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /></svg> |  | ||||||
| Before Width: | Height: | Size: 406 B | 
| @ -1,107 +1,49 @@ | |||||||
| "use client"; | 'use client'; | ||||||
| import { useRef, useState } from "react"; | import { useRef, useState } from "react"; | ||||||
| import { useFileActions } from "~/app/_components/FileActions"; | import { useFileActions } from "~/app/_components/FileActions"; | ||||||
| import toast from "react-hot-toast"; |  | ||||||
| 
 | 
 | ||||||
| export function FileActionsContainer({ | export function FileActionsContainer({ | ||||||
|   fileId, |   fileId, | ||||||
|   fileName, |   fileName, | ||||||
|   fileUrl, |   fileUrl, | ||||||
|   isOwner, |   isOwner, | ||||||
|   isPublic: initialIsPublic, // Rename to avoid conflict with local state
 |  | ||||||
| }: { | }: { | ||||||
|   fileId: string; |   fileId: string; | ||||||
|   fileName: string; |   fileName: string; | ||||||
|   fileUrl: string; |   fileUrl: string; | ||||||
|   isOwner: boolean; |   isOwner: boolean; | ||||||
|   isPublic: boolean; |  | ||||||
| }) { | }) { | ||||||
|   const [isPublic, setIsPublic] = useState(initialIsPublic); // Local state for toggle
 |   const { handleDownload, handleCopyUrl, handleRemove} = useFileActions(() => fileId, (description: string) => { | ||||||
|   const { handleDownload, handleCopyUrl, handleRemove } = useFileActions( |     if (isOwner) { | ||||||
|     () => fileId, |       console.log(description); | ||||||
|     (description: string) => { |  | ||||||
|       if (isOwner) { |  | ||||||
|         console.log(description); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   ); |   }); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className="flex gap-2 self-center"> |     <div className="flex self-center gap-2"> | ||||||
|       {/* Download Button */} |       {/* Download Button */} | ||||||
|       <button |       <button | ||||||
|       onClick={() => handleDownload(fileId, fileName)} |         onClick={() => handleDownload(fileId, fileName)} | ||||||
|       className="flex items-center justify-center rounded-full bg-blue-500 p-2 hover:bg-blue-600" |         className="flex items-center justify-center rounded-full bg-blue-500 p-2 hover:bg-blue-600" | ||||||
|       > |       > | ||||||
|       <img src="/icons/download.svg" alt="Download" className="h-6 w-6" /> |         <img src="/icons/download.svg" alt="Download" className="w-6 h-6" /> | ||||||
|       </button> |       </button> | ||||||
| 
 | 
 | ||||||
|       {/* Copy URL Button */} |       {/* Copy URL Button */} | ||||||
|       <button |       <button | ||||||
|       onClick={() => { |         onClick={() => handleCopyUrl(fileUrl)} | ||||||
|         handleCopyUrl(fileUrl); |         className="flex items-center justify-center rounded-full bg-green-500 p-2 hover:bg-green-600" | ||||||
|         toast.success("File URL copied to clipboard!"); |  | ||||||
|       }} |  | ||||||
|       className="flex items-center justify-center rounded-full bg-green-500 p-2 hover:bg-green-600" |  | ||||||
|       > |       > | ||||||
|       <img src="/icons/copy.svg" alt="Copy URL" className="h-6 w-6" /> |         <img src="/icons/copy.svg" alt="Copy URL" className="w-6 h-6" /> | ||||||
|       </button> |       </button> | ||||||
| 
 | 
 | ||||||
|       {/* Remove Button */} |       {/* Remove Button */} | ||||||
|       {isOwner && ( |  | ||||||
|       <button |       <button | ||||||
|         onClick={async () => { |         onClick={() => handleRemove(fileId)} | ||||||
|         try { |  | ||||||
|           await handleRemove(fileId); |  | ||||||
|           toast.success("File removed successfully!"); |  | ||||||
|         } catch (err) { |  | ||||||
|           toast.error("Failed to remove file."); |  | ||||||
|           console.error(err); |  | ||||||
|         } |  | ||||||
|         }} |  | ||||||
|         className="flex items-center justify-center rounded-full bg-red-500 p-2 hover:bg-red-600" |         className="flex items-center justify-center rounded-full bg-red-500 p-2 hover:bg-red-600" | ||||||
|       > |       > | ||||||
|         <img src="/icons/delete.svg" alt="Remove" className="h-6 w-6" /> |         <img src="/icons/delete.svg" alt="Remove" className="w-6 h-6" /> | ||||||
|       </button> |       </button> | ||||||
|       )} |  | ||||||
| 
 |  | ||||||
|       {/* Public/Private Toggle */} |  | ||||||
|       {isOwner && ( |  | ||||||
|       <button |  | ||||||
|         onClick={async () => { |  | ||||||
|         const newIsPublic = !isPublic; |  | ||||||
|         try { |  | ||||||
|           const response = await fetch(`/api/files/update-public`, { |  | ||||||
|           method: "POST", |  | ||||||
|           headers: { |  | ||||||
|             "Content-Type": "application/json", |  | ||||||
|           }, |  | ||||||
|           body: JSON.stringify({ |  | ||||||
|             fileId: fileId, |  | ||||||
|             isPublic: newIsPublic, |  | ||||||
|           }), |  | ||||||
|           }); |  | ||||||
|           if (!response.ok) { |  | ||||||
|           throw new Error("Failed to update public status"); |  | ||||||
|           } |  | ||||||
|           setIsPublic(newIsPublic); // Update local state
 |  | ||||||
|           toast.success( |  | ||||||
|           `File is now ${newIsPublic ? "Public" : "Private"}!` |  | ||||||
|           ); |  | ||||||
|         } catch (err) { |  | ||||||
|           toast.error("Error updating public status."); |  | ||||||
|           console.error(err); |  | ||||||
|         } |  | ||||||
|         }} |  | ||||||
|         className="flex items-center justify-center rounded-full bg-gray-500 p-2 hover:bg-gray-600" |  | ||||||
|       > |  | ||||||
|         <img |  | ||||||
|         src={isPublic ? "/icons/public.svg" : "/icons/private.svg"} |  | ||||||
|         alt={isPublic ? "Public" : "Private"} |  | ||||||
|         className="h-6 w-6" |  | ||||||
|         /> |  | ||||||
|       </button> |  | ||||||
|       )} |  | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| @ -113,15 +55,12 @@ export function FileDescriptionContainer({ | |||||||
|   fileId: string; |   fileId: string; | ||||||
|   fileDescription?: string; |   fileDescription?: string; | ||||||
| }) { | }) { | ||||||
|  | 
 | ||||||
|   const [description, setDescription] = useState(fileDescription || ""); // Add state for description
 |   const [description, setDescription] = useState(fileDescription || ""); // Add state for description
 | ||||||
|   const { handleDescriptionChange } = useFileActions( |   const { handleDescriptionChange } = useFileActions(() => {}, (description: string) => { | ||||||
|     () => {}, |     setDescription(description); | ||||||
|     (description: string) => { |     return undefined; | ||||||
|       setDescription(description); |   }, fileId); // Wrap setDescription in a function
 | ||||||
|       return undefined; |  | ||||||
|     }, |  | ||||||
|     fileId, |  | ||||||
|   ); // Wrap setDescription in a function
 |  | ||||||
|   const debounceTimer = useRef<NodeJS.Timeout | null>(null); // Initialize debounce timer
 |   const debounceTimer = useRef<NodeJS.Timeout | null>(null); // Initialize debounce timer
 | ||||||
| 
 | 
 | ||||||
|   const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { |   const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { | ||||||
| @ -129,9 +68,9 @@ export function FileDescriptionContainer({ | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className="flex gap-2 self-center"> |     <div className="flex self-center gap-2"> | ||||||
|       <textarea |       <textarea | ||||||
|         className="h-24 w-full rounded-md border bg-gray-800 p-2 text-white" |         className="w-full h-24 p-2 border rounded-md bg-gray-800 text-white" | ||||||
|         value={description} // Use state value
 |         value={description} // Use state value
 | ||||||
|         onChange={handleChange} |         onChange={handleChange} | ||||||
|         placeholder="Enter file description..." |         placeholder="Enter file description..." | ||||||
| @ -139,4 +78,4 @@ export function FileDescriptionContainer({ | |||||||
|       /> |       /> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| @ -15,7 +15,6 @@ interface FileDetails { | |||||||
|   description: string; |   description: string; | ||||||
|   extension: string; |   extension: string; | ||||||
|   isOwner: boolean; // Indicates if the user owns the file
 |   isOwner: boolean; // Indicates if the user owns the file
 | ||||||
|   public: boolean; // Indicates if the file is public
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface FileGridProps { | interface FileGridProps { | ||||||
| @ -117,8 +116,7 @@ export default function FileGrid({ session }: FileGridProps) { | |||||||
|                 fileId={file.id} |                 fileId={file.id} | ||||||
|                 fileName={file.name} |                 fileName={file.name} | ||||||
|                 fileUrl={file.url} |                 fileUrl={file.url} | ||||||
|                 isOwner={true} |                 isOwner={file.isOwner} | ||||||
|                 isPublic={file.public} |  | ||||||
|               /> |               /> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|  | |||||||
| @ -1,25 +0,0 @@ | |||||||
| import { NextResponse } from "next/server"; |  | ||||||
| import { db } from "~/server/db"; |  | ||||||
| 
 |  | ||||||
| export async function GET(req: Request) { |  | ||||||
|   const url = new URL(req.url); |  | ||||||
|   const query = url.searchParams.get("query") || ""; |  | ||||||
| 
 |  | ||||||
|   try { |  | ||||||
|     // if query is empty, return no files
 |  | ||||||
|     const files = await db.file.findMany({ |  | ||||||
|       where: { |  | ||||||
|         OR: [ |  | ||||||
|           { name: { contains: query } }, |  | ||||||
|           { description: { contains: query } }, |  | ||||||
|         ], |  | ||||||
|         public: true, |  | ||||||
|       }, |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     return NextResponse.json({ files }); |  | ||||||
|   } catch (error) { |  | ||||||
|     console.error("Error fetching files:", error); |  | ||||||
|     return NextResponse.json({ error: "Failed to fetch files" }, { status: 500 }); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -32,7 +32,6 @@ export async function GET(req: Request) { | |||||||
|       type: file.extension, |       type: file.extension, | ||||||
|       url: file.url, |       url: file.url, | ||||||
|       description: file.description, |       description: file.description, | ||||||
|       isPublic: file.public, // Ensure this is included
 |  | ||||||
|     }); |     }); | ||||||
|   } catch (error) { |   } catch (error) { | ||||||
|     console.error("Error fetching file details:", error); |     console.error("Error fetching file details:", error); | ||||||
|  | |||||||
| @ -1,22 +0,0 @@ | |||||||
| import { NextResponse } from "next/server"; |  | ||||||
| import { db } from "~/server/db"; |  | ||||||
| 
 |  | ||||||
| export async function POST(req: Request) { |  | ||||||
|   try { |  | ||||||
|     const { fileId, isPublic } = await req.json(); |  | ||||||
| 
 |  | ||||||
|     if (!fileId) { |  | ||||||
|       return NextResponse.json({ error: "File ID is required" }, { status: 400 }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     await db.file.update({ |  | ||||||
|       where: { id: fileId }, |  | ||||||
|       data: { public: isPublic }, |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     return NextResponse.json({ success: true }); |  | ||||||
|   } catch (error) { |  | ||||||
|     console.error("Error updating public status:", error); |  | ||||||
|     return NextResponse.json({ error: "Failed to update public status" }, { status: 500 }); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -8,35 +8,14 @@ import { Toaster } from "react-hot-toast"; | |||||||
| export default async function Home() { | export default async function Home() { | ||||||
|   const session = await auth(); |   const session = await auth(); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <HydrateClient> |     <HydrateClient> | ||||||
|       <Toaster position="top-right" reverseOrder={false} /> |       <Toaster position="top-right" reverseOrder={false} /> | ||||||
|       <main className="relative flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> |       <main className="relative flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> | ||||||
|         {/* Top-right corner sign-out button */} |         {/* Top-right corner sign-out button */} | ||||||
|         {session?.user && ( |         {session?.user && ( | ||||||
|           <div className="absolute top-4 right-4 flex items-center gap-4"> |           <div className="absolute top-4 right-4"> | ||||||
|             {/* Search Button */} |  | ||||||
|             <Link |  | ||||||
|               href="/search" |  | ||||||
|               className="rounded-full bg-white/10 p-2 transition hover:bg-white/20" |  | ||||||
|               aria-label="Search" |  | ||||||
|             > |  | ||||||
|               <svg |  | ||||||
|                 xmlns="http://www.w3.org/2000/svg" |  | ||||||
|                 className="h-6 w-6 text-white" |  | ||||||
|                 fill="none" |  | ||||||
|                 viewBox="0 0 24 24" |  | ||||||
|                 stroke="currentColor" |  | ||||||
|               > |  | ||||||
|                 <path |  | ||||||
|                   strokeLinecap="round" |  | ||||||
|                   strokeLinejoin="round" |  | ||||||
|                   strokeWidth={2} |  | ||||||
|                   d="M21 21l-4.35-4.35M16.65 10.65a6 6 0 11-12 0 6 6 0 0112 0z" |  | ||||||
|                 /> |  | ||||||
|               </svg> |  | ||||||
|             </Link> |  | ||||||
| 
 |  | ||||||
|             <Link |             <Link | ||||||
|               href="/api/auth/signout" |               href="/api/auth/signout" | ||||||
|               className="rounded-full bg-white/10 px-4 py-2 font-semibold no-underline transition hover:bg-white/20" |               className="rounded-full bg-white/10 px-4 py-2 font-semibold no-underline transition hover:bg-white/20" | ||||||
| @ -54,7 +33,7 @@ export default async function Home() { | |||||||
|           {session?.user ? ( |           {session?.user ? ( | ||||||
|             <> |             <> | ||||||
|               <FileGrid session={session} /> |               <FileGrid session={session} /> | ||||||
|               <UploadForm /> |               <UploadForm/> | ||||||
|             </> |             </> | ||||||
|           ) : ( |           ) : ( | ||||||
|             <p className="text-center text-2xl text-white"> |             <p className="text-center text-2xl text-white"> | ||||||
| @ -62,16 +41,16 @@ export default async function Home() { | |||||||
|             </p> |             </p> | ||||||
|           )} |           )} | ||||||
|           {!session?.user && ( |           {!session?.user && ( | ||||||
|             <div className="flex flex-col items-center gap-2"> |           <div className="flex flex-col items-center gap-2"> | ||||||
|               <div className="flex flex-col items-center justify-center gap-4"> |             <div className="flex flex-col items-center justify-center gap-4"> | ||||||
|                 <Link |               <Link | ||||||
|                   href={session ? "/api/auth/signout" : "/api/auth/signin"} |                 href={session ? "/api/auth/signout" : "/api/auth/signin"} | ||||||
|                   className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20" |                 className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20" | ||||||
|                 > |               > | ||||||
|                   {session ? "Sign out" : "Sign in"} |                 {session ? "Sign out" : "Sign in"} | ||||||
|                 </Link> |               </Link> | ||||||
|               </div> |  | ||||||
|             </div> |             </div> | ||||||
|  |           </div> | ||||||
|           )} |           )} | ||||||
|         </div> |         </div> | ||||||
|       </main> |       </main> | ||||||
|  | |||||||
| @ -1,121 +0,0 @@ | |||||||
| "use client"; |  | ||||||
| import { useEffect, useState } from "react"; |  | ||||||
| import { useRouter } from "next/navigation"; |  | ||||||
| import { env } from "~/env.js"; |  | ||||||
| import { FilePreview } from "~/app/_components/FilePreview"; |  | ||||||
| import { FileActionsContainer } from "~/app/_components/ActionButtons"; |  | ||||||
| import { HomeButton } from "~/app/_components/HomeButton"; |  | ||||||
| 
 |  | ||||||
| interface FileDetails { |  | ||||||
|   name: string; |  | ||||||
|   size: number; |  | ||||||
|   owner: string; |  | ||||||
|   ownerAvatar: string | null; |  | ||||||
|   uploadDate: string; |  | ||||||
|   id: string; |  | ||||||
|   isOwner: boolean; |  | ||||||
|   extension: string; |  | ||||||
|   url: string; |  | ||||||
|   description: string; |  | ||||||
|   isPublic: boolean; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export default function SearchFile() { |  | ||||||
|   const [searchQuery, setSearchQuery] = useState<string>(""); |  | ||||||
|   const [files, setFiles] = useState<FileDetails[]>([]); |  | ||||||
|   const [error, setError] = useState<string | null>(null); |  | ||||||
|   const pageUrl = env.NEXT_PUBLIC_PAGE_URL; |  | ||||||
|   const router = useRouter(); |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     const fetchFiles = async () => { |  | ||||||
|       try { |  | ||||||
|         const response = await fetch( |  | ||||||
|           `/api/files/search?query=${encodeURIComponent(searchQuery)}` |  | ||||||
|         ); |  | ||||||
|         if (!response.ok) { |  | ||||||
|           throw new Error("Failed to fetch files"); |  | ||||||
|         } |  | ||||||
|         const data = await response.json(); |  | ||||||
|         setFiles(data.files); |  | ||||||
|       } catch (err) { |  | ||||||
|         console.error(err); |  | ||||||
|         setError("Failed to load files."); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     fetchFiles(); |  | ||||||
|   }, [searchQuery]); |  | ||||||
| 
 |  | ||||||
|   return ( |  | ||||||
|     <main className="flex min-h-screen flex-col items-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white"> |  | ||||||
|       <div className="absolute top-4 left-4 z-20"> |  | ||||||
|         <HomeButton /> |  | ||||||
|       </div> |  | ||||||
|       {/* Search Bar */} |  | ||||||
|       <div className="sticky top-0 z-10 w-full bg-[#2e026d] px-4 py-4 shadow-md"> |  | ||||||
|         <div className="relative w-full max-w-md mx-auto"> |  | ||||||
|           <input |  | ||||||
|             type="text" |  | ||||||
|             placeholder="Search files..." |  | ||||||
|             value={searchQuery} |  | ||||||
|             onChange={(e) => setSearchQuery(e.target.value)} |  | ||||||
|             className="w-full p-3 pl-12 text-white bg-[#3b0764] rounded-full shadow-md focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent placeholder-gray-400" |  | ||||||
|           /> |  | ||||||
|           <svg |  | ||||||
|             xmlns="http://www.w3.org/2000/svg" |  | ||||||
|             className="absolute left-4 top-1/2 transform -translate-y-1/2 h-6 w-6 text-gray-400" |  | ||||||
|             fill="none" |  | ||||||
|             viewBox="0 0 24 24" |  | ||||||
|             stroke="currentColor" |  | ||||||
|           > |  | ||||||
|             <path |  | ||||||
|               strokeLinecap="round" |  | ||||||
|               strokeLinejoin="round" |  | ||||||
|               strokeWidth={2} |  | ||||||
|               d="M21 21l-4.35-4.35M16.65 10.65a6 6 0 11-12 0 6 6 0 0112 0z" |  | ||||||
|             /> |  | ||||||
|           </svg> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
| 
 |  | ||||||
|       {/* File Grid */} |  | ||||||
|       <div className="flex-grow flex flex-col items-center justify-center w-full px-4"> |  | ||||||
|         {error && <div className="text-red-500">{error}</div>} |  | ||||||
|         <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 w-full max-w-7xl"> |  | ||||||
|           {files.map((file) => { |  | ||||||
|             return ( |  | ||||||
|               <div |  | ||||||
|                 key={file.id} |  | ||||||
|                 className="flex place-content-end w-xxs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20" |  | ||||||
|               > |  | ||||||
|                 <div className="self-center max-w-100 sm:max-w-50"> |  | ||||||
|                   <FilePreview fileId={file.id} fileType={file.extension} /> |  | ||||||
|                 </div> |  | ||||||
| 
 |  | ||||||
|                 <button onClick={() => router.push(pageUrl + file.url)}> |  | ||||||
|                   <h3 className="text-2xl font-bold">{file.name}</h3> |  | ||||||
|                 </button> |  | ||||||
|                 {file.description && ( |  | ||||||
|                   <p className="text-sm text-gray-400"> |  | ||||||
|                     Description: {file.description} |  | ||||||
|                   </p> |  | ||||||
|                 )} |  | ||||||
| 
 |  | ||||||
|                 <div className="flex self-center gap-2"> |  | ||||||
|                   <FileActionsContainer |  | ||||||
|                     fileId={file.id} |  | ||||||
|                     fileName={file.name} |  | ||||||
|                     fileUrl={file.url} |  | ||||||
|                     isOwner={false} // Check if the user is the owner
 |  | ||||||
|                     isPublic={file.isPublic} // Check if the file is public
 |  | ||||||
|                   /> |  | ||||||
|                 </div> |  | ||||||
|               </div> |  | ||||||
|             ); |  | ||||||
|           })} |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     </main> |  | ||||||
|   ); |  | ||||||
| } |  | ||||||
| @ -19,7 +19,6 @@ interface FileDetails { | |||||||
|   type: string; |   type: string; | ||||||
|   url: string; |   url: string; | ||||||
|   description: string; |   description: string; | ||||||
|   isPublic: boolean; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function generateMetadata({ | export async function generateMetadata({ | ||||||
| @ -67,9 +66,9 @@ async function fetchFileDetails(fileId: string): Promise<FileDetails | null> { | |||||||
|   try { |   try { | ||||||
|     const response = await fetch( |     const response = await fetch( | ||||||
|       `${process.env.NEXT_PUBLIC_PAGE_URL}/api/files/share?id=${encodeURIComponent( |       `${process.env.NEXT_PUBLIC_PAGE_URL}/api/files/share?id=${encodeURIComponent( | ||||||
|         fileId, |         fileId | ||||||
|       )}`,
 |       )}`,
 | ||||||
|       { cache: "no-store" }, |       { cache: "no-store" } | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     if (!response.ok) { |     if (!response.ok) { | ||||||
| @ -137,10 +136,10 @@ export default async function FilePreviewContainer({ | |||||||
|             {fileDetails.size > 1024 * 1024 * 1024 |             {fileDetails.size > 1024 * 1024 * 1024 | ||||||
|               ? (fileDetails.size / (1024 * 1024 * 1024)).toFixed(2) + " GB" |               ? (fileDetails.size / (1024 * 1024 * 1024)).toFixed(2) + " GB" | ||||||
|               : fileDetails.size > 1024 * 1024 |               : fileDetails.size > 1024 * 1024 | ||||||
|                 ? (fileDetails.size / (1024 * 1024)).toFixed(2) + " MB" |               ? (fileDetails.size / (1024 * 1024)).toFixed(2) + " MB" | ||||||
|                 : fileDetails.size > 1024 |               : fileDetails.size > 1024 | ||||||
|                   ? (fileDetails.size / 1024).toFixed(2) + " KB" |               ? (fileDetails.size / 1024).toFixed(2) + " KB" | ||||||
|                   : fileDetails.size + " Bytes"} |               : fileDetails.size + " Bytes"} | ||||||
|           </p> |           </p> | ||||||
|           <p> |           <p> | ||||||
|             <strong>Owner:</strong>{" "} |             <strong>Owner:</strong>{" "} | ||||||
| @ -167,8 +166,7 @@ export default async function FilePreviewContainer({ | |||||||
|               fileId={fileDetails.id} |               fileId={fileDetails.id} | ||||||
|               fileName={fileDetails.name} |               fileName={fileDetails.name} | ||||||
|               fileUrl={fileDetails.url} |               fileUrl={fileDetails.url} | ||||||
|               isOwner={true} |               isOwner={fileDetails.isOwner} | ||||||
|               isPublic={fileDetails.isPublic} |  | ||||||
|             /> |             /> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								tailwind.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tailwind.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | module.exports = { | ||||||
|  |   content: [ | ||||||
|  |     "./src/**/*.{js,ts,jsx,tsx}", // Ensure your paths are correct
 | ||||||
|  |   ], | ||||||
|  |   plugins: [require("@tailwindcss/typography")], | ||||||
|  | }; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user