From 88641d24fc0dc07ec0f575a42edf6eaca317f353 Mon Sep 17 00:00:00 2001 From: Jae Young Ahn Date: Tue, 22 Apr 2025 09:33:55 -0700 Subject: [PATCH] Assignment Page for instructor --- src/hooks/useAuth.js | 7 ++ src/pages/AssignmentPage.jsx | 119 ++++++++++++++++++++ src/routers/AppRouter.jsx | 10 ++ src/scss/components/ProtectedRoute.jsx | 23 ++++ src/scss/components/_assignment.scss | 150 +++++++++++++++++++++++++ 5 files changed, 309 insertions(+) create mode 100644 src/hooks/useAuth.js create mode 100644 src/pages/AssignmentPage.jsx create mode 100644 src/scss/components/ProtectedRoute.jsx create mode 100644 src/scss/components/_assignment.scss diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js new file mode 100644 index 0000000..20b1395 --- /dev/null +++ b/src/hooks/useAuth.js @@ -0,0 +1,7 @@ +// src/hooks/useAuth.js +import { useContext } from "react"; +import { AuthContext } from "../contexts/AuthContext"; + +export const useAuth = () => { + return useContext(AuthContext); +}; diff --git a/src/pages/AssignmentPage.jsx b/src/pages/AssignmentPage.jsx new file mode 100644 index 0000000..2f24da7 --- /dev/null +++ b/src/pages/AssignmentPage.jsx @@ -0,0 +1,119 @@ +import React, { useState } from "react"; +import "../scss/components/_assignment.scss"; + +const AssignmentPage = () => { + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); + const [file, setFile] = useState(null); + const [projects, setProjects] = useState([]); + const [showModal, setShowModal] = useState(false); + const [editingIndex, setEditingIndex] = useState(null); + + const handleSubmit = async (e) => { + e.preventDefault(); + + const formData = new FormData(); + formData.append("title", title); + formData.append("description", description); + formData.append("file", file); + + try { + const res = await fetch("/api/assignments", { + method: "POST", + body: formData, + }); + + if (res.ok) { + alert("Successfully Uploaded!"); + setProjects([...projects, { title, description }]); + setTitle(""); + setDescription(""); + setFile(null); + setShowModal(false); + } else { + alert("Fail Uploading!"); + } + } catch (err) { + console.error(err); + alert("Server Error!"); + } + }; + + const handleDelete = (index) => { + const updated = projects.filter((_, i) => i !== index); + setProjects(updated); + }; + + return ( +
+

πŸ“˜ Assignments

+ + + {showModal && ( +
+
+

{editingIndex !== null ? "Edit Assignment" : "New Assignment"}

+
+
+ + setTitle(e.target.value)} + required + /> +
+ +
+ + +
+ +
+ + setFile(e.target.files[0])} + required + /> +
+ +
+ + +
+
+
+
+ )} + +
+

πŸ“‹ Projects

+ {projects.map((project, index) => ( +
+

{project.title}

+

{project.description}

+ + +
+ ))} +
+
+ ); +}; + +export default AssignmentPage; diff --git a/src/routers/AppRouter.jsx b/src/routers/AppRouter.jsx index f1b5ffa..0ca9c86 100644 --- a/src/routers/AppRouter.jsx +++ b/src/routers/AppRouter.jsx @@ -14,6 +14,7 @@ import PageNotFound from "../pages/PageNotFound"; import Hero from "../components/Hero"; import Navbar from "../components/Navbar"; import Services from "../components/Services"; +import AssignmentPage from "../pages/AssignmentPage"; const AppRouter = () => { return ( @@ -32,6 +33,14 @@ const AppRouter = () => { } /> + + + // + } + /> } /> { } /> + } /> diff --git a/src/scss/components/ProtectedRoute.jsx b/src/scss/components/ProtectedRoute.jsx new file mode 100644 index 0000000..0f5769c --- /dev/null +++ b/src/scss/components/ProtectedRoute.jsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Navigate } from "react-router-dom"; +import { useAuth } from "../hooks/useAuth"; // μ˜ˆμ‹œ ν›…: λ„ˆμ˜ ν”„λ‘œμ νŠΈμ— 맞게 μˆ˜μ •! + +const ProtectedRoute = ({ children, role }) => { + const { user, isLoading } = useAuth(); + + if (isLoading) { + return
loading...
; + } + + if (!user) { + return ; + } + + if (role && user.role !== role) { + return ; + } + + return children; +}; + +export default ProtectedRoute; diff --git a/src/scss/components/_assignment.scss b/src/scss/components/_assignment.scss new file mode 100644 index 0000000..42a5298 --- /dev/null +++ b/src/scss/components/_assignment.scss @@ -0,0 +1,150 @@ +.assignment-page { + max-width: 600px; + margin: auto; + padding: 20px; + + form { + margin-bottom: 20px; + + div { + margin-bottom: 15px; + + label { + display: block; + font-weight: bold; + margin-bottom: 5px; + } + + input[type="text"], + textarea, + input[type="file"] { + width: 100%; + padding: 8px; + border: 1px solid #ccc; + border-radius: 4px; + } + + textarea { + height: 80px; + resize: vertical; + } + } + + button { + padding: 8px 16px; + background-color: #4285f4; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + + &:hover { + background-color: #3367d6; + } + } + } + + .project-list { + margin-top: 30px; + + .project-item { + border: 1px solid #ddd; + padding: 12px; + margin-bottom: 10px; + border-radius: 6px; + background-color: #f9f9f9; + + h4 { + margin: 0 0 5px 0; + } + + button { + margin-right: 10px; + padding: 6px 10px; + border: none; + border-radius: 4px; + cursor: pointer; + + &:first-of-type { + background-color: #ffa500; + color: white; + + &:hover { + background-color: #e59400; + } + } + + &:last-of-type { + background-color: #e74c3c; + color: white; + + &:hover { + background-color: #c0392b; + } + } + } + } + } + + .modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + + .modal { + background: white; + padding: 20px; + border-radius: 10px; + width: 300px; + + h3 { + margin-bottom: 10px; + } + + input, + textarea { + width: 100%; + margin-bottom: 10px; + padding: 8px; + border: 1px solid #ccc; + border-radius: 4px; + } + + .modal-buttons { + display: flex; + justify-content: space-between; + + button { + padding: 6px 12px; + border: none; + border-radius: 4px; + cursor: pointer; + + &:first-of-type { + background-color: #28a745; + color: white; + + &:hover { + background-color: #218838; + } + } + + &:last-of-type { + background-color: #6c757d; + color: white; + + &:hover { + background-color: #5a6268; + } + } + } + } + } + } +}