-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Document Details Page Backend and Frontend Integration (#164)
* Started work on layout responsiveness * Improved Sidebar responsiveness and typography * Started Working on Document-Details Page * Refactor Sidebar and DropdownMenu components * Update icon usage in PasswordValidation * Refactor document and user interfaces; * Enhance utility functions for file size and date/time formatting with detailed documentation and improved locale support * Refactor useSort hook to improve sorting logic * Renamed Components, hooks, services * Fixed Icon * Implement document-details and link management API endpoints with authentication and error handling * Refactor document details page structure by removing obsolete components and adding new dynamic document view and loading components * Add document not found, view document, loading components, and info table with sorting and pagination * Fix import typo in CreateLink component * Refactor hooks and components * Refactor document components to improve imports and type handling * Update LinkVisitors model to include first and last name fields * Enhance document fetching and user-specific link handling, and enhance link handling logic * Updated Views data on Document details * Refactor visitor data handling to use 'Contact' model and update field names for clarity * Remove error toast from link creation and correct typo in sharing options message
- Loading branch information
Showing
59 changed files
with
3,861 additions
and
2,310 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
Client/src/app/api/documents/[documentId]/links/[linkId]/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { authenticate } from '@lib/middleware/authenticate'; | ||
import prisma from '@lib/prisma'; | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
|
||
export async function DELETE(req: NextRequest, { params }: { params: { linkId: string } }) { | ||
try { | ||
const userId = await authenticate(req); | ||
const { linkId } = params; | ||
|
||
// Verify doc ownership + link existence | ||
const link = await prisma.link.findFirst({ | ||
where: { | ||
linkId: linkId, | ||
userId: userId, | ||
}, | ||
}); | ||
|
||
if (!link) { | ||
return NextResponse.json( | ||
{ error: 'Link not found or you do not have access.' }, | ||
{ status: 404 }, | ||
); | ||
} | ||
|
||
// Delete the link | ||
await prisma.link.delete({ | ||
where: { linkId: link.linkId }, | ||
}); | ||
|
||
return NextResponse.json({ message: 'Link deleted successfully.' }, { status: 200 }); | ||
} catch (error) { | ||
return createErrorResponse('Server error.', 500, error); | ||
} | ||
} | ||
|
||
function createErrorResponse(message: string, status: number, details?: any) { | ||
console.error(`[${new Date().toISOString()}] ${message}`, details); | ||
return NextResponse.json({ error: message, details }, { status }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { authenticate } from '@lib/middleware/authenticate'; | ||
import prisma from '@lib/prisma'; | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
|
||
export async function GET(req: NextRequest, { params }: { params: { documentId: string } }) { | ||
try { | ||
const userId = await authenticate(req); | ||
const { documentId } = params; | ||
|
||
// Verify doc ownership | ||
const doc = await prisma.document.findFirst({ | ||
where: { document_id: documentId, user_id: userId }, | ||
include: { Link: true }, | ||
}); | ||
|
||
if (!doc) { | ||
return NextResponse.json( | ||
{ error: 'Document not found or you do not have access.' }, | ||
{ status: 404 }, | ||
); | ||
} | ||
|
||
// Map the DB fields to the shape needed by InfoTable -> LinkDetail | ||
|
||
const links = doc.Link.map((link) => ({ | ||
id: link.id, | ||
documentId: doc.document_id, | ||
linkId: link.linkId, | ||
createdLink: link.linkUrl, | ||
lastViewed: link.updatedAt, | ||
linkViews: 0, // Placeholder | ||
})); | ||
|
||
return NextResponse.json({ links }, { status: 200 }); | ||
} catch (error) { | ||
return createErrorResponse('Server error.', 500, error); | ||
} | ||
} | ||
|
||
function createErrorResponse(message: string, status: number, details?: any) { | ||
console.error(`[${new Date().toISOString()}] ${message}`, details); | ||
return NextResponse.json({ error: message, details }, { status }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { authenticate } from '@lib/middleware/authenticate'; | ||
import prisma from '@lib/prisma'; | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
import { deleteFile } from '@/services/storageService'; | ||
|
||
export async function GET(req: NextRequest, { params }: { params: { documentId: string } }) { | ||
try { | ||
// Verify the user is logged in | ||
const userId = await authenticate(req); | ||
const { documentId } = params; | ||
|
||
// Query the database for this document, ensuring it belongs to user | ||
const doc = await prisma.document.findFirst({ | ||
where: { document_id: documentId, user_id: userId }, | ||
select: { | ||
id: true, | ||
document_id: true, | ||
fileName: true, | ||
filePath: true, | ||
fileType: true, | ||
size: true, | ||
createdAt: true, | ||
updatedAt: true, | ||
User: { | ||
select: { | ||
first_name: true, | ||
last_name: true, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
// If not found or unauthorized | ||
if (!doc) { | ||
return NextResponse.json( | ||
{ error: 'Document not found or you do not have access.' }, | ||
{ status: 404 }, | ||
); | ||
} | ||
|
||
// Construct the response | ||
const responsePayload = { | ||
...doc, | ||
uploader: { | ||
name: `${doc.User.first_name} ${doc.User.last_name}`, | ||
avatar: null, // Add avatar URL here | ||
}, | ||
links: 0, // placeholder | ||
viewers: 0, // placeholder | ||
views: 0, // placeholder | ||
}; | ||
|
||
return NextResponse.json({ document: responsePayload }, { status: 200 }); | ||
} catch (error) { | ||
return createErrorResponse('Server error.', 500, error); | ||
} | ||
} | ||
|
||
// Utility for error handling | ||
function createErrorResponse(message: string, status: number, details?: any) { | ||
console.error(`[${new Date().toISOString()}] ${message}`, details); | ||
return NextResponse.json({ error: message, details }, { status }); | ||
} | ||
|
||
export async function DELETE( | ||
req: NextRequest, | ||
{ params }: { params: { documentId: string } }, | ||
): Promise<NextResponse> { | ||
try { | ||
const userId = await authenticate(req); | ||
const documentId = params.documentId; | ||
|
||
if (!documentId) { | ||
return createErrorResponse('Document ID is required.', 400); | ||
} | ||
|
||
const document = await prisma.document.findUnique({ | ||
where: { document_id: documentId }, | ||
}); | ||
|
||
if (!document || document.user_id !== userId) { | ||
return createErrorResponse('Document not found or access denied.', 404); | ||
} | ||
|
||
const deletedFile = await prisma.document.delete({ | ||
where: { document_id: documentId }, | ||
}); | ||
|
||
await deleteFileFromStorage(deletedFile.filePath); | ||
|
||
return NextResponse.json({ message: 'Document deleted successfully.' }, { status: 200 }); | ||
} catch (error) { | ||
return createErrorResponse('Server error.', 500, error); | ||
} | ||
} | ||
|
||
async function deleteFileFromStorage(filePath: string) { | ||
try { | ||
await deleteFile(filePath); | ||
} catch (error) { | ||
throw error; | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
Client/src/app/api/documents/[documentId]/visitors/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { authenticate } from '@lib/middleware/authenticate'; | ||
import prisma from '@lib/prisma'; | ||
import { NextRequest, NextResponse } from 'next/server'; | ||
|
||
export async function GET(req: NextRequest, { params }: { params: { documentId: string } }) { | ||
try { | ||
const userId = await authenticate(req); | ||
const { documentId } = params; | ||
|
||
// Ensure doc belongs to user | ||
const doc = await prisma.document.findFirst({ | ||
where: { document_id: documentId, user_id: userId }, | ||
include: { Link: true }, | ||
}); | ||
if (!doc) { | ||
return NextResponse.json( | ||
{ error: 'Document not found or you do not have access.' }, | ||
{ status: 404 }, | ||
); | ||
} | ||
|
||
// Gather all linkIds | ||
const linkIds = doc.Link.map((l) => l.linkId); | ||
if (linkIds.length === 0) { | ||
// No links => no visitors | ||
return NextResponse.json({ visitors: [] }, { status: 200 }); | ||
} | ||
|
||
// Query LinkVisitors for all those linkIds | ||
const linkVisitors = await prisma.linkVisitors.findMany({ | ||
where: { | ||
linkId: { in: linkIds }, | ||
}, | ||
orderBy: { updatedAt: 'desc' }, | ||
}); | ||
|
||
const visitors = linkVisitors.map((visitor) => ({ | ||
id: visitor.id, | ||
documentId: doc.document_id, | ||
name: `${visitor.first_name} ${visitor.last_name}`.trim(), | ||
email: visitor.email, | ||
lastActivity: visitor.updatedAt, | ||
// These are placeholders for now | ||
downloads: 0, | ||
duration: 0, | ||
completion: 0, | ||
})); | ||
|
||
return NextResponse.json({ visitors }, { status: 200 }); | ||
} catch (error) { | ||
return createErrorResponse('Server error.', 500, error); | ||
} | ||
} | ||
|
||
function createErrorResponse(message: string, status: number, details?: any) { | ||
console.error(`[${new Date().toISOString()}] ${message}`, details); | ||
return NextResponse.json({ error: message, details }, { status }); | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.