Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

created the student-dashboard issue table endpoint #172

Merged
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
created the student-dashboard issue table endpoint
swostikpati committed Nov 4, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit bf3fb94cb4c4f4a89812c1f9c9e60a7d7783075c
4 changes: 4 additions & 0 deletions back-end/app.js
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import cors from "cors";
import url from "url";
import path from "path";
import login from "./src/routes/login.js";
import studentIssues from "./src/routes/studentIssues.js";
// import multer from "multer"; - configure when required

const app = express(); // instantiate an Express object
@@ -41,5 +42,8 @@ app.use(morgan("dev"));
// login
app.use("/api/login", login);

// Student Side Issue Retrieval
app.use("/api/issues/student", studentIssues);

// export the express app we created to make it available to other modules
export default app;
2,490 changes: 2,490 additions & 0 deletions back-end/public/json/contextual_mockapi.json

Large diffs are not rendered by default.

File renamed without changes.
19 changes: 19 additions & 0 deletions back-end/src/controllers/studentIssuesHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import axios from "axios";

export async function issueRetrievalHandler(req, res) {
const { paramName } = req.params;

if (paramName === "all") {
try {
// Assuming the data you want is at the response.data property
const response = await axios.get(
`${process.env.BACKEND_URL}/json/contextual_mockapi.json` // will be replaced with db call
);
res.json(response.data);
} catch (error) {
// Log the error and send an appropriate response
console.error("Error retrieving issues:", error.message);
res.status(500);
}
}
}
8 changes: 8 additions & 0 deletions back-end/src/routes/studentIssues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import express from "express";
import { issueRetrievalHandler } from "../controllers/studentIssuesHandler.js";

const router = express.Router();

router.get("/:paramName", issueRetrievalHandler);

export default router;
3 changes: 0 additions & 3 deletions front-end/src/layouts/LoginPage/LoginPage.js
Original file line number Diff line number Diff line change
@@ -31,9 +31,6 @@ const LoginPage = () => {
);
// The response data from the server
auth = response.data.authenticated;

// Now you can use responseData as needed
console.log("Response data:", auth);
} catch (error) {
console.error("Error during form submission:", error);
// In case of error, if you need to access the response provided by the server (if any)
202 changes: 134 additions & 68 deletions front-end/src/layouts/StudentDashboard/StudentDashboard.js
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import StudentViewFilter from "../../components/student/StudentViewFilter/Studen
import DesktopIssueDetails from "../../components/student/StudentIssueOverlay/DesktopIssueDetails";
import SiteWideFooter from "../../components/general/SiteWideFooter/SiteWideFooter";
import { CreateRequest } from "../../components/student/CreateRequest";
import axios from "axios";

const StudentDashboard = () => {
// State initialization for holding requests and their display variant
@@ -16,21 +17,40 @@ const StudentDashboard = () => {
const [request, setRequest] = useState(null);

// API
const apiUrl = "https://hasiburratul.github.io/mock-api/MOCK_DATA.json";
const BASE_URL = process.env.REACT_APP_BACKEND_URL;

// Fetch data from the API
useEffect(() => {
fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
const sortedData = data.sort((a, b) => parseDate(b.dateCreated) - parseDate(a.dateCreated));
setAllRequests(sortedData);
setDisplayedRequests(sortedData);
})
.catch((error) => {
console.error("Error fetching data from API:", error);
});
}, [apiUrl]);
let isMounted = true; // flag to check if component is mounted - to prevent memory leaks

const fetchData = async () => {
try {
const response = await axios.get(`${BASE_URL}/api/issues/student/all`);
const sortedData = response.data.sort(
(a, b) => parseDate(b.dateCreated) - parseDate(a.dateCreated)
);
if (isMounted) {
setAllRequests(sortedData);
setDisplayedRequests(sortedData);
}
console.log("Fetched Data");
} catch (error) {
if (isMounted) {
console.error("Error fetching data from API:", error);
}
}
};

fetchData();

// Interval for polling from the server - 5 seconds
const intervalId = setInterval(fetchData, 5000);

// Clean up interval on unmount
return () => {
clearInterval(intervalId); // clear interval on unmount to prevent memory leaks
isMounted = false; // set flag to false when we know the component will unmount
};
}, []); // Empty dependency array means this effect will only run once on mount

// State initialization for tracking window width and adjusting display
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
@@ -73,7 +93,8 @@ const StudentDashboard = () => {
<th>Title</th>
<th>Description</th>
<th>Departments</th>
<th className="date-created-header">Date Created
<th className="date-created-header">
Date Created
<button onClick={() => toggleSortOrder()} className="sort-button">
{sortOrder === "latestFirst" ? "↑" : "↓"}
</button>
@@ -102,51 +123,56 @@ const StudentDashboard = () => {
};

if (isIssueOverlayOpen) {
document.addEventListener('mousedown', handleOutsideClick);
document.addEventListener("mousedown", handleOutsideClick);
} else {
document.removeEventListener('mousedown', handleOutsideClick);
document.removeEventListener("mousedown", handleOutsideClick);
}

return () => {
document.removeEventListener('mousedown', handleOutsideClick);
document.removeEventListener("mousedown", handleOutsideClick);
};
}, [isIssueOverlayOpen]);

const getStatusClass = (status) => {
switch (status) {
case 'Action Required':
return 'status-action-required';
case 'Resolved':
return 'status-closed';
case 'In Progress':
return 'status-in-progress';
case 'Open':
return 'status-open';
case "Action Required":
return "status-action-required";
case "Resolved":
return "status-closed";
case "In Progress":
return "status-in-progress";
case "Open":
return "status-open";
default:
return '';
return "";
}
};

// Custom date parsing function for "dd/mm/yyyy" format
const parseDate = (dateString) => {
const [day, month, year] = dateString.split('/').map(Number);
const [day, month, year] = dateString.split("/").map(Number);
return new Date(year, month - 1, day); // Month is 0-based in JavaScript
};

const sortRequests = (order) => {
const sortedRequests = [...displayedRequests];

if (order === "latestFirst") {
sortedRequests.sort((a, b) => parseDate(b.dateCreated) - parseDate(a.dateCreated));
sortedRequests.sort(
(a, b) => parseDate(b.dateCreated) - parseDate(a.dateCreated)
);
} else if (order === "oldestFirst") {
sortedRequests.sort((a, b) => parseDate(a.dateCreated) - parseDate(b.dateCreated));
sortedRequests.sort(
(a, b) => parseDate(a.dateCreated) - parseDate(b.dateCreated)
);
}

setDisplayedRequests(sortedRequests);
};

const toggleSortOrder = () => {
const newSortOrder = sortOrder === "latestFirst" ? "oldestFirst" : "latestFirst";
const newSortOrder =
sortOrder === "latestFirst" ? "oldestFirst" : "latestFirst";
setSortOrder(newSortOrder);
sortRequests(newSortOrder);
};
@@ -158,18 +184,24 @@ const StudentDashboard = () => {

if (windowWidth <= 768) {
return (
<tr key={index} onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}>
<td className="title-cell-mobile">
{request.title}
</td>
<tr
key={index}
onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}
>
<td className="title-cell-mobile">{request.title}</td>
<td>
<span className={`status-box ${getStatusClass(request.currentStatus)}` } onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}>
<span
className={`status-box ${getStatusClass(
request.currentStatus
)}`}
onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}
>
{request.currentStatus}
</span>
</td>
@@ -180,17 +212,23 @@ const StudentDashboard = () => {
// It is only for Desktop view for now
return (
<tr key={index}>
<td className="title-cell" onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}>
<td
className="title-cell"
onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}
>
{request.title}
</td>

<td className="description-cell" onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}>
<td
className="description-cell"
onClick={() => {
setIsIssueOverlayOpen(true);
setRequest(request.index);
}}
>
{truncatedDescription}
</td>

@@ -201,11 +239,13 @@ const StudentDashboard = () => {
</span>
))}
</td>
<td className="date-created-cell">
{request.dateCreated}
</td>
<td className="date-created-cell">{request.dateCreated}</td>
<td>
<span className={`status-box ${getStatusClass(request.currentStatus)}`}>
<span
className={`status-box ${getStatusClass(
request.currentStatus
)}`}
>
{request.currentStatus}
</span>
</td>
@@ -249,8 +289,8 @@ const StudentDashboard = () => {
}

if (selectedStatus !== "") {
filteredRequests = filteredRequests.filter((request) =>
request.currentStatus === selectedStatus
filteredRequests = filteredRequests.filter(
(request) => request.currentStatus === selectedStatus
);
}

@@ -288,8 +328,8 @@ const StudentDashboard = () => {
}

if (status !== "") {
filteredRequests = filteredRequests.filter((request) =>
request.currentStatus === status
filteredRequests = filteredRequests.filter(
(request) => request.currentStatus === status
);
}

@@ -305,7 +345,7 @@ const StudentDashboard = () => {
// Placeholder for the "Create Request" functionality
const handleCreateRequest = () => {
setIsCreateRequestVisible(!isCreateRequestVisible);
};
};

// Handles pagination and page switching
const handlePageChange = (page) => {
@@ -329,7 +369,7 @@ const StudentDashboard = () => {
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
>
{'«'}
{"«"}
</button>
);

@@ -364,7 +404,7 @@ const StudentDashboard = () => {
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
>
{'»'}
{"»"}
</button>
);

@@ -419,8 +459,9 @@ const StudentDashboard = () => {

return (
<>
<div className={`requests ${isIssueOverlayOpen ? 'blur-background' : ''}`}>

<div
className={`requests ${isIssueOverlayOpen ? "blur-background" : ""}`}
>
<StudentNavbar studentName={studentName} />

<h2 className="h2-student-dashboard">Your Requests</h2>
@@ -434,14 +475,29 @@ const StudentDashboard = () => {
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={handleKeyDown}
/>
<button className="button-student-dashboard" onClick={handleSearch}>Search</button>
<button className="button-student-dashboard" onClick={handleSearch}>
Search
</button>
</div>

<StudentViewFilter filterHandler={handleFilterByDepartment} selectedOption={selectedDepartment} options={departmentOptions} />
<StudentViewFilter filterHandler={handleFilterByStatus} selectedOption={selectedStatus} options={statusOptions} />
<StudentViewFilter
filterHandler={handleFilterByDepartment}
selectedOption={selectedDepartment}
options={departmentOptions}
/>
<StudentViewFilter
filterHandler={handleFilterByStatus}
selectedOption={selectedStatus}
options={statusOptions}
/>

<div className="create-request-button">
<button className="button-student-dashboard" onClick={handleCreateRequest}>Create Request +</button>
<button
className="button-student-dashboard"
onClick={handleCreateRequest}
>
Create Request +
</button>
</div>
</div>

@@ -454,13 +510,23 @@ const StudentDashboard = () => {
<div className="pagination">
<div className="pagination-box">{renderPagination()}</div>
</div>
{isCreateRequestVisible && <CreateRequest isVisible={isCreateRequestVisible} onClose={handleCreateRequest} />}
{isCreateRequestVisible && (
<CreateRequest
isVisible={isCreateRequestVisible}
onClose={handleCreateRequest}
/>
)}
</div>
{/* The Overlay popup is triggered by clicking on the title or description */}
{/* It is only for Desktop view for now */}
{isIssueOverlayOpen && (
<div className="issueOverlay" ref={overlayRef}>
<button className="closeButton issue-buttons" onClick={closeIssueOverlay}>X</button>
<button
className="closeButton issue-buttons"
onClick={closeIssueOverlay}
>
X
</button>

<DesktopIssueDetails index={request} />
</div>