Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const fs = require("node:fs").promises; // For asynchronous file writing
const path = require("node:path");
const app = express();
const port = 3001; // Or another port
var mammoth = require("mammoth");

app.use(cors());
app.use(express.json()); // To parse JSON request bodies
Expand Down Expand Up @@ -48,6 +49,48 @@ app.post("/api/files/:fileId/comments", async (req, res) => {
}
});

app.get("/api/files/:fileId/content", (req, res) => {
const fileId = req.params.fileId;
const file = data.files.find((row) => row.id === fileId);

if( !file ) {
return res.status(404).json({ error: "File not found!" });
}
const file_type = path.extname(file.filename)
if ( file_type !== '.docx' ) {
return res.status(422).json({ error: "Incorrect file type!" });
}
mammoth.convertToHtml({path: path.join(__dirname, 'data', file.filename)})
.then(function(result){
res.send(result.value)
})
.catch(function(error) {
console.error("Error converting file", error)
res.status(500).json({ error: `Error converting file: ${error.message}` });
});
});

app.get("/api/files/:fileId/view", (req, res) => {
const fileId = req.params.fileId;
const file = data.files.find((row) => row.id === fileId);

if( !file ) {
return res.status(404).json({ error: "File not found!" });
}
const file_type = path.extname(file.filename)
if ( file_type !== '.pdf' ) {
return res.status(422).json({ message: "Preview not supported" });
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent error response format. This endpoint uses 'message' property while other error responses use 'error' property. Should be consistent across the API.

Suggested change
return res.status(422).json({ message: "Preview not supported" });
return res.status(422).json({ error: "Preview not supported" });

Copilot uses AI. Check for mistakes.
}
try {
return res
.setHeader("Content-Type", "application/pdf")
.sendFile(path.join(__dirname, 'data', file.filename))
} catch( error ) {
console.error("Error viewing PDF file", error)
res.status(500).json({ error: `Error viewing PDF file: ${error.message}` });
}
});

app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
57 changes: 57 additions & 0 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import CommentSection from "@/components/CommentSection"; // Import the componen
interface File {
id: string;
name: string;
filename: string
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the filename property declaration. This is inconsistent with the other interface properties.

Suggested change
filename: string
filename: string;

Copilot uses AI. Check for mistakes.
uploadDate: string;
}

Expand All @@ -16,6 +17,9 @@ export default function Home() {
const [selectedFileId, setSelectedFileId] = useState<string | null>(null); // State for selected file ID
const [isLoadingFiles, setIsLoadingFiles] = useState(false);
const [errorFiles, setErrorFiles] = useState<string | null>(null);
const [file, setFile] = useState<string | null>(null);
const [fileType, setFileType] = useState<"pdf"|"docx"|null>(null);
const [fileLoading, setFileLoading] = useState<boolean>(false);

useEffect(() => {
setIsLoadingFiles(true);
Expand Down Expand Up @@ -53,6 +57,35 @@ export default function Home() {
}
};

useEffect(() => {
setFileLoading(true)
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the statement. This is inconsistent with the coding style used elsewhere in the file.

Suggested change
setFileLoading(true)
setFileLoading(true);

Copilot uses AI. Check for mistakes.
const file = files.find((row) => row.id === selectedFileId)
if( !file ) {
console.error("file not found!")
setFileLoading(false)
return
Comment on lines +64 to +66
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the statement. This is inconsistent with the coding style used elsewhere in the file.

Suggested change
console.error("file not found!")
setFileLoading(false)
return
console.error("file not found!");
setFileLoading(false);
return;

Copilot uses AI. Check for mistakes.
}
getFileDetails(file)
}, [selectedFileId, files])


function getFileDetails(file: File) {
const file_split = file.filename.split('.')
const file_type = file_split.slice(-1)[0]
if( file_type === "docx" ) {
setFileType("docx")
fetch(`http://localhost:3001/api/files/${file.id}/content`)
.then(async (res) => {
setFile(await res.text());
})
} else if ( file_type === "pdf" ) {
setFileType("pdf")
setFile(`http://localhost:3001/api/files/${file.id}/view#view=FitV`);
}

setFileLoading(false)
}

return (
<main className="flex min-h-screen flex-col p-8 md:p-16 lg:p-24 bg-gray-50 text-gray-900">
<div className="w-full max-w-6xl mx-auto">
Expand Down Expand Up @@ -95,10 +128,34 @@ export default function Home() {

{/* Comment Section */}
<div className="md:col-span-2">

{fileLoading && <p>Loading files...</p>}
{
!!file ? (<>
{ fileType == 'docx' ? <DocxDiv html={file} /> : <PDFDiv link={file} /> }
</>): undefined}
<CommentSection fileId={selectedFileId} />
</div>
</div>
</div>
</main>
);
}

function DocxDiv({ html }: {
html: string
})
{
return (
<div dangerouslySetInnerHTML={{__html: html}}></div>
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using dangerouslySetInnerHTML with unsanitized HTML content from the server creates a potential XSS vulnerability. Consider sanitizing the HTML content before rendering.

Copilot uses AI. Check for mistakes.
)
}

function PDFDiv({ link }: {
link: string
})
{
return (
<iframe src={link} width={750} height={1000} />
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iframe is missing important security attributes. Consider adding 'sandbox' and 'title' attributes for better security and accessibility.

Suggested change
<iframe src={link} width={750} height={1000} />
<iframe src={link} width={750} height={1000} sandbox="allow-scripts allow-same-origin" title="PDF Viewer" />

Copilot uses AI. Check for mistakes.
)
}