diff --git a/devU-client/src/components/pages/submissions/submissionFileView.tsx b/devU-client/src/components/pages/submissions/submissionFileView.tsx index 8a31cd7f..687a53a6 100644 --- a/devU-client/src/components/pages/submissions/submissionFileView.tsx +++ b/devU-client/src/components/pages/submissions/submissionFileView.tsx @@ -1,11 +1,13 @@ import React, { useEffect, useState } from 'react' -import { useParams } from 'react-router-dom' +import { useParams, useHistory } from 'react-router-dom' import RequestService from 'services/request.service' import { Submission } from 'devu-shared-modules' -import {Document, Page} from 'react-pdf' +// import { Document, Page } from 'react-pdf' import { pdfjs } from 'react-pdf' +import 'react-pdf/dist/Page/TextLayer.css'; import PageWrapper from 'components/shared/layouts/pageWrapper' import { getToken } from 'utils/authentication.utils' +import PDFViewer from 'components/utils/pdfViewer' import config from '../../../config' const proxiedUrls = { @@ -14,14 +16,14 @@ const proxiedUrls = { function _replaceUrl(userUrl: string) { const proxy: string | undefined = Object.keys(proxiedUrls).find((key) => { - if (userUrl.startsWith(key)) return true - return false + if (userUrl.startsWith(key)) return true + return false }) - + if (!proxy) return userUrl - + return userUrl.replace(proxy, proxiedUrls[proxy as keyof typeof proxiedUrls]) - } +} pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`; @@ -30,11 +32,11 @@ const SubmissionFileView = () => { const { courseId, assignmentId, submissionId } = useParams<{ courseId: string, assignmentId: string, submissionId: string }>() const [bucket, setBucket] = useState('') const [filename, setFilename] = useState('') + const history = useHistory() const authToken = getToken() const [file, setFile] = useState(null) - const [numPages, setNumPages] = useState(0) - + useEffect(() => { fetchData() }, []) @@ -48,14 +50,14 @@ const SubmissionFileView = () => { const fetchData = async () => { try { await RequestService.get(`/api/course/${courseId}/assignment/${assignmentId}/submissions/${submissionId}`) - .then((data) => { - const submissionFiles = JSON.parse(data.content) - const [tempbucket,tempfilename] = submissionFiles.filepaths[0].split('/') - setBucket(tempbucket) - setFilename(tempfilename) + .then((data) => { + const submissionFiles = JSON.parse(data.content) + const [tempbucket, tempfilename] = submissionFiles.filepaths[0].split('/') + setBucket(tempbucket) + setFilename(tempfilename) - }) + }) } catch (e) { console.error(e) } @@ -74,39 +76,36 @@ const SubmissionFileView = () => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } - + const arrayBuffer = await response.arrayBuffer(); const blob = new Blob([arrayBuffer], { type: 'application/pdf' }); const file = new File([blob], filename, { type: 'application/pdf' }); - setFile(file); } catch (e) { console.error(e) } } - const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => { - setNumPages(numPages) - } - return ( -
- {file && - - {[...Array(numPages)].map((_, index) => ( -
- -
- ))} -
- } +
+

View Files

+ +
+
+ {file ? ( + + ) : ( +

No files found for this submission

+ )}
-
+
) } -export default SubmissionFileView \ No newline at end of file +export default SubmissionFileView; \ No newline at end of file diff --git a/devU-client/src/components/utils/pdfViewer.scss b/devU-client/src/components/utils/pdfViewer.scss new file mode 100644 index 00000000..34d41b8f --- /dev/null +++ b/devU-client/src/components/utils/pdfViewer.scss @@ -0,0 +1,49 @@ +@import 'variables'; + +.viewPdfWrapper { + display: flex; + gap: 20px; +} + +.annotationOptions { + position: sticky; + height: fit-content; + top: 40px; +} + +.highlightColors { + min-width: 200px; + display: flex; + justify-content: flex-start; + gap: 10px; + margin-top: 20px; + // background-color: #555; +} + +.colorBtn { + width: min-content; + padding: 5px 10px; + font-weight: 700; + color: $text-color; + border-radius: 5px; +} + +.blue { + border: 2px solid rgba(0, 225, 255, 0.75); + background-color:rgba(0, 225, 255, 0.25); +} + +.red { + border: 2px solid rgba(255, 60, 0, 0.75); + background-color: rgba(255, 60, 0, 0.25); +} + +.green { + border: 2px solid rgba(80, 255, 0, 0.75); + background-color:rgba(80, 255, 0, 0.25); +} + +.yellow { + border: 2px solid rgba(255, 247, 0, 0.75); + background-color:rgba(255, 247, 0, 0.25); +} diff --git a/devU-client/src/components/utils/pdfViewer.tsx b/devU-client/src/components/utils/pdfViewer.tsx new file mode 100644 index 00000000..f473dd3e --- /dev/null +++ b/devU-client/src/components/utils/pdfViewer.tsx @@ -0,0 +1,178 @@ +import React, { useState, useRef } from 'react'; +import { Document, Page } from 'react-pdf'; +import "react-pdf/dist/esm/Page/TextLayer.css"; +import "react-pdf/dist/esm/Page/AnnotationLayer.css"; +import { useAppSelector } from 'redux/hooks' +import styles from "./pdfViewer.scss"; + +interface PDFWithHighlightProps { + file: Blob | File; +} + +interface Highlight { + color: string, + text: string, + rect: { + top: number; + left: number; + width: number; + height: number; + } +} + +const PDFViewer: React.FC = ({ file }) => { + const [highlights, setHighlights] = useState([]); + const [numPages, setNumPages] = useState(0); + const [annotate, setAnnotate] = useState(false); + const [annotationColor, setAnnotationColor] = useState('rgba(0, 225, 255, 0.25)'); + const [annotationBorderColor, setAnnotationBorderColor] = useState('rgba(0, 225, 255, 0.75)'); + const containerRef = useRef(null); + const role = useAppSelector((store) => store.roleMode) + + const [startPoint, setStartPoint] = useState<{ x: number; y: number } | null>(null); + const [currentRect, setCurrentRect] = useState(null); + + const handleMouseDown = (e: React.MouseEvent) => { + if (!containerRef.current) return; + const containerRect = containerRef.current.getBoundingClientRect(); + setStartPoint({ + x: e.clientX - containerRect.left, + y: e.clientY - containerRect.top, + }); + }; + + const handleMouseMove = (e: React.MouseEvent) => { + if (!startPoint || !containerRef.current) return; + const containerRect = containerRef.current.getBoundingClientRect(); + const x = e.clientX - containerRect.left; + const y = e.clientY - containerRect.top; + + const width = Math.abs(x - startPoint.x); + const height = Math.abs(y - startPoint.y); + + setCurrentRect({ + left: Math.min(x, startPoint.x), + top: Math.min(y, startPoint.y), + width, + height, + }); + }; + + const handleMouseUp = () => { + if (currentRect) { + const text = prompt("Enter annotation text:"); + setHighlights((prev) => [ + ...prev, + { + color: annotationColor, + rect: currentRect, + text: text || "", // Add text to the highlight object + }, + ]); + } + setStartPoint(null); + setCurrentRect(null); + }; + + const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => { + setNumPages(numPages) + } + + const toggleAnnotate = () => { + setAnnotate(!annotate); + } + + const setColors = (e: React.MouseEvent) => { + const computedStyle = window.getComputedStyle(e.currentTarget); + const color = computedStyle.backgroundColor; + const borderColor = color.replace("0.25", "0.75"); + console.log(borderColor); + + setAnnotationColor(color); + + // color is in rgba. change the opacity to 0.75 + setAnnotationBorderColor(borderColor); + } + + return ( +
+
+ {/* react-pdf document */} + + {[...Array(numPages)].map((_, index) => { + const pageNumber = index + 1; + return ( + + ); + })} + + {/* Render current rectangle while dragging */} + {annotate && currentRect && ( +
+ )} + {/* Show all highlights */} + {highlights.map((highlight, index) => ( +
{highlight.text}
+ ))} +
+ + {/* annotation options */} + {role.isInstructor() && (
+ + {annotate && (
+ + + + +
)} +
)} +
+ ); +}; + +export default PDFViewer;