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 index ede6235..5bd8855 100644 --- a/src/pages/AssignmentPage.jsx +++ b/src/pages/AssignmentPage.jsx @@ -1,107 +1,191 @@ -// Page - Assignment -import { useEffect, useState } from 'react'; -import '../scss/styles.scss'; +import React, { useState } from "react"; +import "../scss/components/_assignment.scss"; const AssignmentPage = () => { - const [files, setFiles] = useState([]); - - useEffect(() => { - document.title = 'Assignment'; - }, []); + const [studentName, setStudentName] = useState(""); + const [campID, setCampID] = useState(""); + const [programID, setProgramID] = useState(""); + const [password, setPassword] = useState(""); + 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 handleFileChange = (e) => { - if (e.target.files) { - const newFiles = Array.from(e.target.files); - setFiles(prevFiles => [...prevFiles, ...newFiles]); - } + const resetForm = () => { + setStudentName(""); + setCampID(""); + setProgramID(""); + setPassword(""); + setTitle(""); + setDescription(""); + setFile(null); + setEditingIndex(null); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + + const newProject = { + studentName, + campID, + programID, + title, + description, + fileName: file ? file.name : null, }; - const handleRemoveFile = (index) => { - setFiles(prevFiles => prevFiles.filter((_, i) => i !== index)); - }; + if (editingIndex !== null) { + // Edit mode: update the project at the index + const updatedProjects = [...projects]; + updatedProjects[editingIndex] = newProject; + setProjects(updatedProjects); + } else { + // New submission + setProjects([...projects, newProject]); + } - const handleSubmit = (e) => { - e.preventDefault(); - // Here you would typically send the files to a server - console.log('Files to submit:', files); - alert('Assignment submitted successfully!'); - }; + alert(editingIndex !== null ? "Assignment updated!" : "Assignment submitted!"); + resetForm(); + setShowModal(false); + }; - return ( -
-
-
-

Assignment Submission

-
-

Due on Jan 16, 2025 11:59 PM

-
-
- -
-
-

Submit Assignment

-

({files.length}) file(s) to submit

-

After uploading, you must click Submit to complete the submission.

-
- -
-
- - - -
- - {files.length > 0 && ( -
-

Selected Files:

-
    - {files.map((file, index) => ( -
  • - {file.name} - ({(file.size / 1024).toFixed(2)} KB) - -
  • - ))} -
-
- )} -
- -
-

Comments

- -
- -
- - -
-
-
-
- ); + const handleEdit = (index) => { + const project = projects[index]; + setStudentName(project.studentName); + setCampID(project.campID); + setProgramID(project.programID); + setTitle(project.title); + setDescription(project.description); + setFile(null); // File can't be set again for editing, usually. You could add note about this. + setEditingIndex(index); + setShowModal(true); + }; + + const handleDelete = (index) => { + const updated = projects.filter((_, i) => i !== index); + setProjects(updated); + }; + + return ( +
+

πŸ“˜ Assignments

+ + + {showModal && ( +
+
+

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

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

πŸ“‹ Projects

+ {projects.map((project, index) => ( +
+
+ Student Name: {project.studentName} | CampID: {project.campID} | ProgramID: {project.programID} +
+

{project.title}

+

{project.description}

+ {project.fileName && ( +

Uploaded File: {project.fileName}

+ )} + +
+ + + +
+
+ ))} +
+ +
+ ); }; export default AssignmentPage; diff --git a/src/routers/AppRouter.jsx b/src/routers/AppRouter.jsx index c4ff117..6f85e45 100644 --- a/src/routers/AppRouter.jsx +++ b/src/routers/AppRouter.jsx @@ -16,6 +16,7 @@ import Hero from "../components/Hero"; import Navbar from "../components/Navbar"; import Services from "../components/Services"; + const AppRouter = () => { return ( @@ -31,7 +32,15 @@ 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..7d424b8 --- /dev/null +++ b/src/scss/components/_assignment.scss @@ -0,0 +1,238 @@ +.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: 2rem; + + h3 { + color: #4a90e2; + } + + .project-item { + background: #ffffff; + border: 1px solid #e0e0e0; + border-radius: 12px; + padding: 1.5rem; + margin-bottom: 1.5rem; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); + transition: transform 0.2s ease, box-shadow 0.2s ease; + + // &:hover { + // transform: translateY(-2px); + // box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08); + // } + + .project-meta { + justify-content: space-between; + align-items: center; + margin-bottom: 0.75rem; + flex-wrap: wrap; + + strong { + color: #34495e; + } + } + + h4 { + margin: 0.5rem 0; + font-size: 1.2rem; + color: #2d3436; + } + + p { + margin: 0.25rem 0; + color: #555; + line-height: 1.4; + + strong { + color: #2d3436; + } + } + + .action-buttons { + display: flex; + gap: 0.5rem; + margin-top: 0.75rem; + + button { + background-color: #f4f4f4; + border: 1px solid #ddd; + border-radius: 6px; + padding: 0.4rem 0.8rem; + cursor: pointer; + font-size: 0.9rem; + transition: background-color 0.2s ease; + + &:hover { + background-color: #e9ecef; + } + + &:nth-child(1) { + color: #2c3e50; + } + + &:nth-child(2) { + color: #c0392b; + } + + &:nth-child(3) { + color: #16a085; + } + } + } + } + } + + + .modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + } + + .modal { + background: #fff; + padding: 2rem; + border-radius: 12px; + max-width: 500px; + width: 100%; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); + animation: fadeIn 0.3s ease; + } + + .modal h3 { + margin-bottom: 1rem; + font-size: 1.5rem; + font-weight: 600; + color: #333; + } + + .modal form > div { + margin-bottom: 1rem; + } + + .modal label { + display: block; + font-size: 0.9rem; + font-weight: 500; + margin-bottom: 0.4rem; + color: #555; + } + + .modal input[type="text"], + .modal input[type="password"], + .modal input[type="file"], + .modal textarea { + width: 100%; + padding: 0.6rem 0.8rem; + border: 1px solid #ccc; + border-radius: 6px; + font-size: 0.95rem; + transition: border-color 0.2s ease; + + &:focus { + border-color: #007bff; + outline: none; + } + } + + .modal textarea { + resize: vertical; + min-height: 80px; + } + + .modal-buttons { + display: flex; + justify-content: flex-end; + gap: 1rem; + margin-top: 1.5rem; + } + + .modal-buttons button { + padding: 0.6rem 1.2rem; + font-size: 0.95rem; + border: none; + border-radius: 6px; + cursor: pointer; + transition: background 0.2s ease; + } + + .modal-buttons button[type="submit"] { + background-color: #ff4b2b; + color: #fff; + + &:hover { + background-color: #FF2600; + } + } + + .modal-buttons button[type="button"] { + background-color: #e0e0e0; + color: #333; + + &:hover { + background-color: #cfcfcf; + } + } + + @keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } + } +} \ No newline at end of file