diff --git a/package.json b/package.json index 976b641..b0eb1f3 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "drizzle-orm": "^0.33.0", "geist": "^1.3.1", "lucide-react": "^0.441.0", - "next": "^14.2.12", + "next": "^14.2.13", "next-themes": "^0.3.0", "pdf2pic": "^3.1.3", "postgres": "^3.4.4", diff --git a/src/app/api/content/[...path]/route.ts b/src/app/api/content/[...path]/route.ts index 08bb858..907ba0e 100644 --- a/src/app/api/content/[...path]/route.ts +++ b/src/app/api/content/[...path]/route.ts @@ -12,12 +12,32 @@ export async function GET( return new NextResponse('Unauthorized', { status: 401 }); } + // Construct the file path relative to the project root const filePath = path.join(process.cwd(), 'content', ...params.path); + console.log('Attempting to read file:', filePath); // Add this log + try { const file = await fs.readFile(filePath); const response = new NextResponse(file); - response.headers.set('Content-Type', 'application/pdf'); + + // Determine content type based on file extension + const ext = path.extname(filePath).toLowerCase(); + switch (ext) { + case '.pdf': + response.headers.set('Content-Type', 'application/pdf'); + break; + case '.png': + response.headers.set('Content-Type', 'image/png'); + break; + case '.jpg': + case '.jpeg': + response.headers.set('Content-Type', 'image/jpeg'); + break; + default: + response.headers.set('Content-Type', 'application/octet-stream'); + } + return response; } catch (error) { console.error('Error reading file:', error); diff --git a/src/app/api/forms/[id]/route.ts b/src/app/api/forms/[id]/route.ts index cefe0f4..8727aa9 100644 --- a/src/app/api/forms/[id]/route.ts +++ b/src/app/api/forms/[id]/route.ts @@ -26,6 +26,7 @@ export async function DELETE( .select({ contentId: informedConsentForms.contentId, location: contents.location, + previewLocation: contents.previewLocation, }) .from(informedConsentForms) .innerJoin(contents, eq(informedConsentForms.contentId, contents.id)) @@ -35,14 +36,21 @@ export async function DELETE( return NextResponse.json({ error: 'Form not found' }, { status: 404 }); } - // Delete the file from the file system - const fullPath = path.join(process.cwd(), form.location); + // Delete the file and preview from the file system + const fullPath = path.join(process.cwd(), form.location ?? ''); + const previewPath = path.join(process.cwd(), form.previewLocation ?? ''); try { await fs.access(fullPath); await fs.unlink(fullPath); } catch (error) { console.warn(`File not found or couldn't be deleted: ${fullPath}`); } + try { + await fs.access(previewPath); + await fs.unlink(previewPath); + } catch (error) { + console.warn(`Preview file not found or couldn't be deleted: ${previewPath}`); + } // Delete the form and content from the database await db.transaction(async (tx) => { @@ -50,7 +58,7 @@ export async function DELETE( await tx.delete(contents).where(eq(contents.id, form.contentId)); }); - return NextResponse.json({ message: "Form deleted successfully" }); + return NextResponse.json({ message: "Form and preview deleted successfully" }); } catch (error) { console.error('Error deleting form:', error); return NextResponse.json({ error: 'Failed to delete form' }, { status: 500 }); diff --git a/src/app/api/forms/route.ts b/src/app/api/forms/route.ts index 6a778ce..1f4d48e 100644 --- a/src/app/api/forms/route.ts +++ b/src/app/api/forms/route.ts @@ -5,7 +5,7 @@ import { auth } from "@clerk/nextjs/server"; import { eq } from "drizzle-orm"; import { saveFile } from "~/lib/fileStorage"; import fs from 'fs/promises'; - +import { studies, participants } from "~/server/db/schema"; // Function to generate a random string const generateRandomString = (length: number) => { const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; @@ -28,10 +28,14 @@ export async function GET(request: Request) { location: contents.location, previewLocation: contents.previewLocation, studyId: informedConsentForms.studyId, + studyTitle: studies.title, participantId: informedConsentForms.participantId, + participantName: participants.name, contentId: informedConsentForms.contentId, }).from(informedConsentForms) - .innerJoin(contents, eq(informedConsentForms.contentId, contents.id)); + .innerJoin(contents, eq(informedConsentForms.contentId, contents.id)) + .innerJoin(studies, eq(informedConsentForms.studyId, studies.id)) + .innerJoin(participants, eq(informedConsentForms.participantId, participants.id)); return NextResponse.json(forms); } diff --git a/src/components/forms/FormCard.tsx b/src/components/forms/FormCard.tsx index 1fd694c..effaac5 100644 --- a/src/components/forms/FormCard.tsx +++ b/src/components/forms/FormCard.tsx @@ -1,6 +1,6 @@ +import Image from 'next/image'; import { Card, CardContent, CardFooter } from "~/components/ui/card"; import { Badge } from "~/components/ui/badge"; -import { Button } from "~/components/ui/button"; import { Trash2 } from "lucide-react"; interface FormCardProps { @@ -12,36 +12,45 @@ interface FormCardProps { studyTitle: string; participantId: number; participantName: string; - previewLocation: string; // Added this property + previewLocation: string; }; onDelete: (formId: number) => void; } export function FormCard({ form, onDelete }: FormCardProps) { + const handleCardClick = () => { + window.open(form.location, '_blank'); + }; + return ( - - - + + {form.title} { + e.currentTarget.src = '/placeholder-image.png'; + console.error('Error loading image:', form.previewLocation); + }} /> -

{form.title}

+
+

{form.title}

+ { + e.stopPropagation(); + onDelete(form.id); + }} + /> +
{form.studyTitle} {form.participantName}
-
); diff --git a/src/lib/fileStorage.ts b/src/lib/fileStorage.ts index 62380b5..d63aeee 100644 --- a/src/lib/fileStorage.ts +++ b/src/lib/fileStorage.ts @@ -28,7 +28,13 @@ export async function saveFile(file: File, filePath: string, previewContentTypeI }; const convert = fromBuffer(buffer, options); - await convert(1); + const result = await convert(1); + + // Rename the file to remove the ".1" suffix + const generatedFilePath = result.path; + if (generatedFilePath) { + await fs.rename(generatedFilePath, previewPath); + } // Return relative paths that can be used in URLs return { diff --git a/src/middleware.ts b/src/middleware.ts index 3c3df2b..ca66c6b 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -14,5 +14,7 @@ export const config = { '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', // Always run for API routes '/(api|trpc)(.*)', + // Add this line to include the /content route + '/content/(.*)', ], } \ No newline at end of file