diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..5aee630
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,65 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "edge",
+ "request": "launch",
+ "name": "Launch Chrome against localhost",
+ "url": "http://localhost:8080",
+ "webRoot": "${workspaceFolder}"
+ },
+ {
+ "type": "pwa-msedge",
+ "name": "Launch Microsoft Edge",
+ "request": "launch",
+ "runtimeArgs": [
+ "--remote-debugging-port=9222"
+ ],
+ "url": "c:\\Users\\tilan\\.vscode\\extensions\\ms-edgedevtools.vscode-edge-devtools-2.1.8\\out\\startpage\\index.html", // Provide your project's url to finish configuring
+ "presentation": {
+ "hidden": true
+ }
+ },
+ {
+ "type": "pwa-msedge",
+ "name": "Launch Microsoft Edge in headless mode",
+ "request": "launch",
+ "runtimeArgs": [
+ "--headless",
+ "--remote-debugging-port=9222"
+ ],
+ "url": "c:\\Users\\tilan\\.vscode\\extensions\\ms-edgedevtools.vscode-edge-devtools-2.1.8\\out\\startpage\\index.html", // Provide your project's url to finish configuring
+ "presentation": {
+ "hidden": true
+ }
+ },
+ {
+ "type": "vscode-edge-devtools.debug",
+ "name": "Open Edge DevTools",
+ "request": "attach",
+ "url": "c:\\Users\\tilan\\.vscode\\extensions\\ms-edgedevtools.vscode-edge-devtools-2.1.8\\out\\startpage\\index.html", // Provide your project's url to finish configuring
+ "presentation": {
+ "hidden": true
+ }
+ }
+ ],
+ "compounds": [
+ {
+ "name": "Launch Edge Headless and attach DevTools",
+ "configurations": [
+ "Launch Microsoft Edge in headless mode",
+ "Open Edge DevTools"
+ ]
+ },
+ {
+ "name": "Launch Edge and attach DevTools",
+ "configurations": [
+ "Launch Microsoft Edge",
+ "Open Edge DevTools"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/App.css b/src/App.css
index 74b5e05..4199fa7 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,38 +1,133 @@
-.App {
- text-align: center;
+/* General Styling */
+body {
+ font-family: 'Source Sans Pro', sans-serif;
+ background-color: #f8f9fa;
+ margin: 0;
+ padding: 0;
}
-.App-logo {
- height: 40vmin;
- pointer-events: none;
+/* Page Header Styling */
+.page-header {
+ background: linear-gradient(to right, #007bff, #6610f2);
+ padding: 30px 0;
+ text-align: center;
+ border-radius: 10px;
+ margin-bottom: 20px;
+ box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
}
-@media (prefers-reduced-motion: no-preference) {
- .App-logo {
- animation: App-logo-spin infinite 20s linear;
- }
+.page-header h1 {
+ font-family: 'Oxygen', sans-serif;
+ font-size: 2rem;
+ font-weight: bold;
+ color: white;
+ margin: 0;
}
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
+.breadcrumb {
display: flex;
- flex-direction: column;
- align-items: center;
justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
+ gap: 10px;
+ padding: 0;
+ margin-bottom: 10px;
+ list-style: none;
+}
+
+.breadcrumb li {
+ font-size: 1rem;
+}
+
+.breadcrumb li a {
+ color: #f8f9fa;
+ text-decoration: none;
+ transition: all 0.3s ease-in-out;
+}
+
+.breadcrumb li a:hover {
+ text-decoration: underline;
+ color: #ffea00;
+}
+
+/* Statistics Cards */
+.stat-card {
+ padding: 20px;
+ text-align: center;
+ font-weight: bold;
+ border-radius: 10px;
+ box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
+ transition: all 0.3s ease-in-out;
+}
+
+.stat-card:hover {
+ transform: scale(1.05);
+ box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.2);
+}
+
+/* Chart Cards */
+.chart-card {
+ padding: 20px;
+ text-align: center;
+ border-radius: 10px;
+ box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
+ background: white;
+ transition: all 0.3s ease-in-out;
}
-.App-link {
- color: #61dafb;
+.chart-card:hover {
+ transform: scale(1.02);
+ box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.2);
}
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
+/* Responsive Design */
+@media (max-width: 768px) {
+ .container {
+ padding: 15px;
}
- to {
- transform: rotate(360deg);
+
+ .page-header {
+ padding: 20px 0;
+ }
+
+ .page-header h1 {
+ font-size: 1.5rem;
+ }
+
+ .breadcrumb {
+ font-size: 0.9rem;
}
+
+ .stat-card {
+ padding: 15px;
+ }
+
+ .chart-card {
+ padding: 15px;
+ }
+}
+
+/* User Profile Card Styling */
+.user-profile-card {
+ border: none;
+ border-radius: 15px;
+ overflow: hidden;
+}
+
+.user-profile-img {
+ width: 150px;
+ height: 150px;
+ object-fit: cover;
+}
+
+.user-name {
+ font-size: 1.25rem;
+ font-weight: bold;
+}
+
+.user-email {
+ font-size: 1rem;
+}
+
+.user-contact {
+ font-size: 1rem;
+ margin-top: 10px;
}
diff --git a/src/App.js b/src/App.js
index 83757e1..20e149f 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,41 +1,82 @@
import React from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
+import { Navbar, Nav, NavDropdown, Container } from "react-bootstrap";
import UserViewModel from "./viewmodels/UserViewModel";
+import "bootstrap/dist/css/bootstrap.min.css";
+import "./App.css";
+// Import Components
+import DashBoard from "./components/DashBoard";
+import AssignTasksMonitoring from "./components/AssignTasksMonitoring";
import UserList from "./components/UserList";
import AddUserForm from "./components/AddUserForm";
import AssignTasks from "./components/AssignTasks";
import TaskList from "./components/TaskList";
-import DashBoard from "./components/DashBoard";
+import Footer from "./components/Footer";
+import TaskDetails from "./components/TaskDetails";
+import EditTask from "./components/EditTask";
+import ReadUser from "./components/UserDetails";
+import EditUser from "./components/EditUser";
const App = () => {
- const { handleusers, handleAddUser, handleAssignTasks, handleDashboard} = UserViewModel();
+ let handleusers, handleAddUser, handleAssignTasks, handleDashboard, handleassignTasksMonitoring, handleDelete;
+
+ try {
+ ({ handleusers, handleAddUser, handleAssignTasks, handleDashboard, handleassignTasksMonitoring, handleDelete } = UserViewModel());
+ } catch (error) {
+ console.error("Error initializing UserViewModel:", error);
+ }
return (
-
Management App
-
- {/* Navigation Links */}
-
+ {/* Navigation Bar */}
+
+
+ Task Manager
+
+
+
+
+
+
{/* Routes */}
-
- } />
- } />
- } />
- } />
- } />
+
+ } />
+ } />
+ } />
+ } />
+ } />
+
+ {/* User Routes */}
+ } />
+ } />
+ } />
+
+ {/* Task Routes */}
+ } />
+ } />
+
+ {/* Footer */}
+
);
};
diff --git a/src/components/AddUserForm.jsx b/src/components/AddUserForm.jsx
index ff1c127..9677829 100644
--- a/src/components/AddUserForm.jsx
+++ b/src/components/AddUserForm.jsx
@@ -1,198 +1,243 @@
import React, { useState } from "react";
-import { Form, Button, Container, Row, Col } from 'react-bootstrap';
+import { Form, Button, Container, Row, Col, Card } from 'react-bootstrap';
const AddUserForm = () => {
- const [user, setUser] = useState({
- firstName: '',
- middleName: '',
- lastName: '',
- dob: '',
- address: '',
- country: '',
- contactNo: '',
- email: ''
- });
-
- const handleChange = (e) => {
- setUser({ ...user, [e.target.name]: e.target.value });
- };
-
- const handleSubmit = async (e) => {
- e.preventDefault();
-
- try {
- const response = await fetch('https://your-api-url/api/user', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(user),
+ const [user, setUser] = useState({
+ firstName: '',
+ middleName: '',
+ lastName: '',
+ department: '',
+ dob: '',
+ address: '',
+ country: '',
+ contactNo: '',
+ email: '',
+ verifyEmail: ''
+ });
+
+ const handleChange = (e) => {
+ setUser({ ...user, [e.target.name]: e.target.value });
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ if (user.email !== user.verifyEmail) {
+ alert('Emails do not match!');
+ return;
+ }
+
+ try {
+ const response = await fetch('https://localhost:44346/api/users/add', { // Ensure correct API URL
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ firstName: user.firstName,
+ middleName: user.middleName,
+ lastName: user.lastName,
+ department: user.department,
+ dob: user.dob,
+ address: user.address,
+ country: user.country,
+ contactNo: user.contactNo,
+ email: user.email
+ }),
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ alert(data.message || 'User created successfully!');
+ setUser({
+ firstName: '',
+ middleName: '',
+ lastName: '',
+ department: '',
+ dob: '',
+ address: '',
+ country: '',
+ contactNo: '',
+ email: '',
+ verifyEmail: ''
});
-
- if (response.ok) {
- alert('User created successfully!');
- setUser({
- firstName: '',
- middleName: '',
- lastName: '',
- dob: '',
- address: '',
- country: '',
- contactNo: '',
- email: ''
- });
- } else {
- alert('Error creating user!');
- }
- } catch (error) {
- console.error('Error:', error);
- alert('Error creating user!');
+ } else {
+ alert(data.message || 'Error creating user!');
}
- };
-
- return (
-
-
-
- Add Employers
-
- Address
-
-
-
-
-
-
- Country
-
-
-
-
-
- Contact No
-
-
-
-
-
-
-
-
- Email
-
-
-
-
-
- Verify Email
-
-
-
-
-
-
-
-
-
-
-
- );
+ } catch (error) {
+ console.error('Error:', error);
+ alert('Error creating user!');
+ }
};
-
- export default AddUserForm;
+
+ return (
+
+
+
+
+
+ Add New User
+ Fill out the form below to add a new user.
+
+
+
+ Address
+
+
+
+
+
+
+ Country
+
+
+
+
+
+ Contact No
+
+
+
+
+
+
+
+
+ Email
+
+
+
+
+
+ Verify Email
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default AddUserForm;
diff --git a/src/components/AssignTasks.jsx b/src/components/AssignTasks.jsx
index 0dbb80f..4d85e15 100644
--- a/src/components/AssignTasks.jsx
+++ b/src/components/AssignTasks.jsx
@@ -1,159 +1,206 @@
-import React, { useState } from 'react';
-import { Form, Button, Container, Row, Col } from 'react-bootstrap';
+import React, { useState } from "react";
+import { Form, Button, Container, Row, Col, Card } from "react-bootstrap";
const AssignTask = () => {
const [task, setTask] = useState({
- title: '',
- assignee: '',
- dueDate: '',
- category: '',
- description: '',
- assignor: '',
- uploadDocument: null,
+ title: "",
+ // assignee: "",
+ dueDate: "",
+ category: "",
+ description: "",
+ // assignor: "",
+ uploadedDocs: null,
});
+ // const [assignee, setAssignee] = useState([]);
+
+ // useEffect(() => {
+ // // Fetch assignees from API
+ // const fetchAssignees = async () => {
+ // try {
+ // const response = await fetch(
+ // "https://localhost:44346/api/users/assignees"
+ // );
+ // if (!response.ok) throw new Error("Failed to fetch assignees");
+
+ // const data = await response.json();
+ // setAssignee(data);
+ // } catch (error) {
+ // console.error("Error fetching assignees:", error);
+ // }
+ // };
+
+ // fetchAssignees();
+ // }, []);
+
const handleChange = (e) => {
setTask({ ...task, [e.target.name]: e.target.value });
};
const handleFileChange = (e) => {
- setTask({ ...task, uploadDocument: e.target.files[0] });
+ const file = e.target.files[0];
+ if (file && file.size > 0) {
+ // Validate file type (PDF, PNG, or JPEG)
+ if (!['application/pdf', 'image/png', 'image/jpeg'].includes(file.type)) {
+ alert('Invalid file type. Only PDF, PNG, and JPEG are allowed.');
+ return;
+ }
+ setTask({ ...task, uploadedDocs: file });
+ }
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
- formData.append('title', task.title);
- formData.append('assignee', task.assignee);
- formData.append('dueDate', task.dueDate);
- formData.append('category', task.category);
- formData.append('description', task.description);
- formData.append('assignor', task.assignor);
- if (task.uploadDocument) {
- formData.append('uploadDocument', task.uploadDocument);
+ formData.append("title", task.title);
+ // formData.append("assignee", task.assignee);
+ formData.append("dueDate", task.dueDate);
+ formData.append("category", task.category);
+ formData.append("description", task.description);
+ formData.append("assignor", task.assignor);
+ if (task.uploadedDocs) {
+ formData.append("uploadedDocs", task.uploadedDocs);
}
try {
- const response = await fetch('https://your-api-url/api/tasks', {
- method: 'POST',
- body: formData, // Using FormData to handle file uploads
+ const response = await fetch("https://localhost:44346/api/tasks/add", {
+ // insert api
+ method: "POST",
+ body: formData,
+ headers: {},
});
if (response.ok) {
- alert('Task assigned successfully!');
+ alert("Task assigned successfully!");
setTask({
- title: '',
- assignee: '',
- dueDate: '',
- category: '',
- description: '',
- assignor: '',
- uploadDocument: null,
+ title: "",
+ // assignee: "",
+ dueDate: "",
+ category: "",
+ description: "",
+ // assignor: "",
+ uploadedDocs: null,
});
} else {
- alert('Error assigning task!');
+ alert("Error assigning task!");
}
} catch (error) {
- console.error('Error:', error);
- alert('Error assigning task!');
+ console.error("Error:", error);
+ alert("Error assigning task!");
}
};
return (
-
-
- Assign Task
-
- Title
-
-
-
-
- Assignee
-
-
-
-
-
-
- Due Date
-
-
-
-
- Category
-
-
-
-
- Description
-
-
-
-
- Who is the Assignor
-
-
-
-
- Upload Document
-
-
-
-
-
-
-
+
+
+
+
+ Assign Task
+ Fill out the form below to add a new task.
+
+
+
+ Title
+
+
+
+ {/*
+ Assignee
+
+
+ {assignee.length > 0 ? (
+ assignee.map((user) => (
+
+ ))
+ ) : (
+
+ )}
+
+ */}
+
+
+ Due Date
+
+
+
+
+ Category
+
+
+
+
+ Description
+
+
+
+ {/*
+ Who is the Assignor
+
+ */}
+
+
+ Upload Document
+
+
+
+
+
+
+
+
+
diff --git a/src/components/AssignTasksMonitoring.jsx b/src/components/AssignTasksMonitoring.jsx
new file mode 100644
index 0000000..2cd3624
--- /dev/null
+++ b/src/components/AssignTasksMonitoring.jsx
@@ -0,0 +1,217 @@
+import React, { useEffect, useState } from "react";
+import { Card, Button, Container, Form, Row, Col, Spinner, Collapse } from "react-bootstrap";
+import '../css/AssignTasksMonitoring.css'; // Import custom CSS styles
+
+const AssignTasksMonitoring = () => {
+ const [tasks, setTasks] = useState([]);
+ const [assignees, setAssignees] = useState([]);
+ const [assignors, setAssignors] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [expandedTask, setExpandedTask] = useState(null); // Track expanded task
+
+ useEffect(() => {
+ const fetchData = async () => {
+ await Promise.all([fetchTasks(), fetchAssignees(), fetchAssignors()]);
+ setLoading(false);
+ };
+ fetchData();
+ }, []);
+
+ // Fetch Tasks
+ const fetchTasks = async () => {
+ try {
+ const response = await fetch("https://localhost:44346/api/tasks/informations");
+ if (!response.ok) throw new Error("Failed to fetch tasks");
+
+ const data = await response.json();
+ setTasks(data);
+ } catch (error) {
+ console.error("Error fetching tasks:", error);
+ }
+ };
+
+ // Fetch Assignees List
+ const fetchAssignees = async () => {
+ try {
+ const response = await fetch("https://localhost:44346/api/users");
+ if (!response.ok) throw new Error("Failed to fetch assignees");
+
+ const data = await response.json();
+ setAssignees(data);
+ } catch (error) {
+ console.error("Error fetching assignees:", error);
+ }
+ };
+
+ // Fetch Assignors List
+ const fetchAssignors = async () => {
+ try {
+ const response = await fetch("https://localhost:44346/api/assignors");
+ if (!response.ok) throw new Error("Failed to fetch assignors");
+
+ const data = await response.json();
+ setAssignors(data);
+ } catch (error) {
+ console.error("Error fetching assignors:", error);
+ }
+ };
+
+ // Handle Task Deletion
+ const handleDelete = async (id) => {
+ if (window.confirm("Are you sure you want to delete this task?")) {
+ try {
+ const response = await fetch(`https://localhost:44346/api/tasks/${id}`, {
+ method: "DELETE",
+ });
+
+ if (response.ok) {
+ setTasks((prevTasks) => prevTasks.filter((task) => task.TaskID !== id));
+ } else {
+ alert("Error deleting task!");
+ }
+ } catch (error) {
+ console.error("Error deleting task:", error);
+ }
+ }
+ };
+
+ // Handle Input Changes
+ const handleChange = (e, id) => {
+ const { name, value } = e.target;
+ setTasks((prevTasks) =>
+ prevTasks.map((task) =>
+ task.TaskID === id ? { ...task, [name]: value } : task
+ )
+ );
+ };
+
+ const toggleDetails = (taskId) => {
+ setExpandedTask((prev) => (prev === taskId ? null : taskId));
+ };
+
+ return (
+
+ {/* Page Header */}
+
+
+
+ Task Monitoring
+
+
+
+ {loading ? (
+
+ ) : (
+
+ {tasks.length > 0 ? (
+ tasks.map((task) => (
+
+
+
+
+ {task.Title}
+
+
+
+
+
+ Due Date: {task.DueDate || "No due date"}
+ Category: {task.Category}
+ Description: {task.Description}
+ Uploaded: {task.UploadedDocs ? "Yes" : "No"}
+
+
+ {/* Assignee Dropdown */}
+
+ Assignee
+ handleChange(e, task.TaskID)}
+ disabled={!assignees.length}
+ >
+
+ {assignees.map((user) => (
+
+ ))}
+
+
+
+ {/* Assignor Dropdown */}
+
+ Assignor
+ handleChange(e, task.TaskID)}
+ disabled={!assignors.length}
+ >
+
+ {assignors.map((assignor) => (
+
+ ))}
+
+
+
+ {/* Status Dropdown */}
+
+ Status
+ handleChange(e, task.TaskID)}
+ >
+
+
+
+
+
+ {/* Action Buttons */}
+
+
+
+
+
+
+
+
+
+
+ ))
+ ) : (
+
+ )}
+
+ )}
+
+ );
+};
+
+export default AssignTasksMonitoring;
diff --git a/src/components/DashBoard.jsx b/src/components/DashBoard.jsx
index 16be4b1..be7afe2 100644
--- a/src/components/DashBoard.jsx
+++ b/src/components/DashBoard.jsx
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
-import { Card, Row, Col } from "react-bootstrap";
+import { Card, Row, Col, Container } from "react-bootstrap";
import { Bar, Pie } from "react-chartjs-2";
import {
@@ -11,98 +11,114 @@ import {
Title,
Tooltip,
Legend
- } from "chart.js";
-
- // Register elements
- ChartJS.register(ArcElement, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
+} from "chart.js";
- const Dashboard = () => {
- const [tasks, setTasks] = useState([]);
- const [employees, setEmployees] = useState([]);
+// Register elements
+ChartJS.register(ArcElement, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
- // Fetch task data
- useEffect(() => {
- fetch("http://localhost:5000/api/tasks") //API url
- .then((res) => res.json())
- .then((data) => setTasks(data))
- .catch((error) => console.error("Error fetching tasks:", error));
+const Dashboard = () => {
+ const [tasks, setTasks] = useState([]);
+ const [employees, setEmployees] = useState([]);
- fetch("http://localhost:5000/api/employees") //API url
- .then((res) => res.json())
- .then((data) => setEmployees(data))
- .catch((error) => console.error("Error fetching employees:", error));
+ // Fetch task data
+ useEffect(() => {
+ fetch("http://localhost:5000/api/tasks")
+ .then((res) => res.json())
+ .then((data) => setTasks(data))
+ .catch((error) => console.error("Error fetching tasks:", error));
- }, []);
+ fetch("http://localhost:5000/api/employees")
+ .then((res) => res.json())
+ .then((data) => setEmployees(data))
+ .catch((error) => console.error("Error fetching employees:", error));
+ }, []);
- // Count completed vs pending tasks
- const completedTasks = tasks.filter(task => task.status === "Completed").length;
- const pendingTasks = tasks.length - completedTasks;
+ // Count completed vs pending tasks
+ const completedTasks = tasks.filter(task => task.status === "Completed").length;
+ const pendingTasks = tasks.length - completedTasks;
- // Employee statistics
- const totalEmployees = employees.length;
- const unassignedEmployees = employees.filter((emp) => emp.assignedTask === null).length;
+ // Employee statistics
+ const totalEmployees = employees.length;
+ const unassignedEmployees = employees.filter((emp) => emp.assignedTask === null).length;
+ // Chart Data
+ const taskData = {
+ labels: ["Completed", "Pending"],
+ datasets: [
+ {
+ label: "Tasks Overview",
+ data: [completedTasks, pendingTasks],
+ backgroundColor: ["#28a745", "#ffc107"],
+ },
+ ],
+ };
- // Chart Data
- const taskData = {
- labels: ["Completed", "Pending"],
- datasets: [
- {
- label: "Tasks Overview",
- data: [completedTasks, pendingTasks],
- backgroundColor: ["#28a745", "#ffc107"],
- },
- ],
- };
+ return (
+
+ {/* Page Header */}
+
+
+
+
+
+
+
+
+
+ Dashboard
+
+
+
+
- return (
-
-
Dashboard
+ {/* Task and Employee Statistics */}
+
+
+
+ Total Employees
+ {totalEmployees}
+
+
+
+
+ Completed Tasks
+ {completedTasks}
+
+
+
+
+ Ongoing Tasks
+ {pendingTasks}
+
+
+
+
+ Unassigned Employees
+ {unassignedEmployees}
+
+
+
- {/* Task and Employee Statistics */}
-
-
-
- Total Employees
- {totalEmployees}
-
-
-
-
- Completed Tasks
- {completedTasks}
-
-
-
-
- Ongoing Tasks
- {pendingTasks}
-
-
-
-
- Unassigned Employees
- {unassignedEmployees}
-
-
-
-
-
-
-
- Task Overview
-
-
-
-
-
- Task Status
-
-
-
-
-
- );
+ {/* Charts */}
+
+
+
+ Task Overview
+
+
+
+
+
+ Task Status
+
+
+
+
+
+ );
};
export default Dashboard;
diff --git a/src/components/EditTask.jsx b/src/components/EditTask.jsx
new file mode 100644
index 0000000..8abcd05
--- /dev/null
+++ b/src/components/EditTask.jsx
@@ -0,0 +1,138 @@
+import React, { useState, useEffect } from "react";
+import { useParams, useNavigate } from "react-router-dom";
+import { Container, Form, Button, Alert, Spinner } from "react-bootstrap";
+
+const EditTask = () => {
+ const { id } = useParams(); // Get task ID from URL
+ const navigate = useNavigate();
+
+ const [task, setTask] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(false);
+
+ useEffect(() => {
+ const fetchTask = async () => {
+ try {
+ console.log(`Fetching Task: ID=${id}`);
+
+ const response = await fetch(`https://localhost:44346/api/tasks/details/${id}`);
+ if (!response.ok) throw new Error("Failed to fetch task details");
+
+ const data = await response.json();
+ console.log("API Response Data:", data);
+
+ if (!data || (Array.isArray(data) && data.length === 0)) {
+ throw new Error("Task not found or empty response.");
+ }
+
+ setTask(data);
+ } catch (error) {
+ console.error("Fetch Error:", error);
+ setError(error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchTask();
+ }, [id]);
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setTask((prevTask) => ({
+ ...prevTask,
+ [name]: value,
+ }));
+ };
+
+ const handleUpdate = async (e) => {
+ e.preventDefault();
+ setSuccess(false);
+ setError(null);
+
+ if (!task) {
+ setError("Task data is not loaded yet!");
+ return;
+ }
+
+ try {
+ const response = await fetch(`https://localhost:44346/api/tasks/update/${id}`, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(task),
+ });
+
+ if (!response.ok) throw new Error("Failed to update task");
+
+ setSuccess(true);
+ setTimeout(() => navigate("/task-list"), 2000);
+ } catch (error) {
+ setError(error.message);
+ }
+ };
+
+ if (loading) return ;
+ if (error) return {error};
+ if (!task) return No task data found.;
+
+ return (
+
+ Edit Task
+ {success && Task updated successfully!}
+ {error && {error}}
+
+
+ Title
+
+
+
+
+ Due Date
+
+
+
+
+ Category
+
+
+
+
+ Description
+
+
+
+
+
+
+ );
+};
+
+export default EditTask;
diff --git a/src/components/EditUser.jsx b/src/components/EditUser.jsx
new file mode 100644
index 0000000..c9e7fa9
--- /dev/null
+++ b/src/components/EditUser.jsx
@@ -0,0 +1,199 @@
+import React, { useState, useEffect } from "react";
+import { useParams, useNavigate } from "react-router-dom";
+import { Container, Form, Button, Alert, Spinner, Card, Row, Col } from "react-bootstrap";
+
+const EditUser = () => {
+ const { id, firstname, lastname } = useParams(); // Get user ID from URL
+ const navigate = useNavigate();
+
+ const [user, setUser] = useState(null); // Initially null
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(false);
+
+ // Fetch user details when component mounts
+ useEffect(() => {
+ const fetchUser = async () => {
+ try {
+ console.log(`Fetching User: ID=${id}, FirstName=${firstname}, LastName=${lastname}`);
+
+ const response = await fetch(`https://localhost:44346/api/users/details/${id}/${firstname}/${lastname}`);
+ if (!response.ok) throw new Error(`Failed to fetch user details: ${response.statusText}`);
+
+ const data = await response.json();
+ console.log("API Response Data:", data); // Debugging API response
+
+ if (!data || (Array.isArray(data) && data.length === 0)) {
+ throw new Error("User not found or empty response.");
+ }
+
+ // If API returns an array, take the first element
+ setUser(Array.isArray(data) ? data[0] : data);
+ } catch (error) {
+ console.error("Fetch Error:", error);
+ setError(error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchUser();
+ }, [id, firstname, lastname]);
+
+ // Handle form input change
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setUser((prevUser) => ({
+ ...prevUser,
+ [name]: value,
+ }));
+ };
+
+ // Handle update submission
+ const handleUpdate = async (e) => {
+ e.preventDefault();
+ setSuccess(false);
+ setError(null);
+
+ if (!user) {
+ setError("User data is not loaded yet!");
+ return;
+ }
+
+ try {
+ console.log("Submitting updated user data:", user); // Debugging log
+
+ const response = await fetch(`https://localhost:44346/api/users/update/${id}`, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(user),
+ });
+
+ if (!response.ok) {
+ const errorMsg = await response.text();
+ throw new Error(`Failed to update user: ${errorMsg}`);
+ }
+
+ setSuccess(true);
+ setTimeout(() => navigate("/user-list"), 2000); // Redirect after success
+ } catch (error) {
+ console.error("Update Error:", error);
+ setError(error.message);
+ }
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ {error}
+
+ );
+ }
+
+ if (!user) {
+ return (
+
+ No user data found.
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ Edit User
+
+
+ {success && User updated successfully!}
+
+
+ First Name
+
+
+
+
+ Middle Name
+
+
+
+
+ Last Name
+
+
+
+
+ Email
+
+
+
+
+ Contact No
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default EditUser;
diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx
new file mode 100644
index 0000000..e2d1775
--- /dev/null
+++ b/src/components/Footer.jsx
@@ -0,0 +1,45 @@
+import React from "react";
+// import { Container, Row, Col } from "react-bootstrap";
+
+const Footer = () => {
+ return (
+
+ );
+};
+
+export default Footer;
diff --git a/src/components/TaskDetails.jsx b/src/components/TaskDetails.jsx
new file mode 100644
index 0000000..2f2f82f
--- /dev/null
+++ b/src/components/TaskDetails.jsx
@@ -0,0 +1,90 @@
+import React, { useEffect, useState } from "react";
+import { Modal, Button, Spinner, Alert, Card } from "react-bootstrap";
+
+const TaskDetailsModal = ({ show, onHide, taskID, category }) => {
+ const [task, setTask] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ if (show) {
+ const fetchReadTask = async () => {
+ try {
+ const response = await fetch(`https://localhost:44346/api/tasks/details/${taskID}/${category}`);
+ if (!response.ok) {
+ throw new Error("Failed to fetch task details");
+ }
+ const data = await response.json();
+ if (!Array.isArray(data) || data.length === 0) {
+ throw new Error("Task not found or empty response.");
+ }
+ setTask(data[0]);
+ } catch (error) {
+ setError(error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+ fetchReadTask();
+ }
+ }, [show, taskID, category]);
+
+ const formattedDueDate = task?.DueDate
+ ? new Date(task.DueDate).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ })
+ : "No due date";
+
+ return (
+
+
+ Task Details
+
+
+ {loading ? (
+
+
+
Loading task details...
+
+ ) : error ? (
+ {error}
+ ) : (
+
+
+
+
+
+ {task?.Title ?? "N/A"}
+ {task?.Category ?? "Uncategorized"}
+
+
+ Due Date: {formattedDueDate}
+
+
+ Category: {task?.Category ?? "No category available"}
+
+
+ Description: {task?.Description ?? "No description available"}
+
+
+
+ )}
+
+
+
+
+
+ );
+};
+
+export default TaskDetailsModal;
diff --git a/src/components/TaskList.jsx b/src/components/TaskList.jsx
index e1beb78..8ee70ab 100644
--- a/src/components/TaskList.jsx
+++ b/src/components/TaskList.jsx
@@ -1,48 +1,200 @@
import React, { useEffect, useState } from "react";
-import { Table, Button } from "react-bootstrap";
+import { Table, Button, Container, Row, Col, Alert, Modal } from "react-bootstrap"; // Added Modal
+import { useNavigate } from "react-router-dom";
+import TaskDetailsModal from "./TaskDetails";
const TaskList = () => {
const [tasks, setTasks] = useState([]);
+ const [error, setError] = useState(null);
+ const [selectedTask, setSelectedTask] = useState(null);
+ const [showModal, setShowModal] = useState(false);
+ const [showDeleteModal, setShowDeleteModal] = useState(false); // Modal state for delete confirmation
+ const navigate = useNavigate();
useEffect(() => {
- fetch("http://localhost:5000/api/tasks")
- .then((res) => res.json())
- .then((data) => setTasks(data))
- .catch((error) => console.error("Error fetching tasks:", error));
+ fetchTasks();
}, []);
+ const fetchTasks = async () => {
+ try {
+ const response = await fetch("https://localhost:44346/api/tasks/informations");
+
+ if (!response.ok) {
+ throw new Error("Failed to fetch tasks");
+ }
+
+ const data = await response.json();
+ console.log("Fetched tasks:", data);
+
+ if (!Array.isArray(data) || data.length === 0) {
+ throw new Error("No tasks found or empty response.");
+ }
+
+ // Ensure TaskID is valid and fallback to a unique identifier if necessary
+ const tasksWithValidIDs = data.map((task, index) => ({
+ ...task,
+ TaskID: task.TaskID || index + 1, // Use index as fallback if TaskID is missing or zero
+ }));
+
+ setTasks(tasksWithValidIDs);
+ } catch (error) {
+ console.error("Error fetching tasks:", error);
+ setError(error.message);
+ }
+ };
+
+ const handleDelete = async () => {
+ if (selectedTask) {
+ try {
+ const response = await fetch(
+ `https://localhost:44346/api/tasks/details/${selectedTask.TaskID}`,
+ {
+ method: "DELETE",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ }
+ );
+
+ if (!response.ok) {
+ const errorMsg = await response.text();
+ throw new Error(`Error deleting task: ${errorMsg}`);
+ }
+
+ setTasks((prevTasks) =>
+ prevTasks.filter((task) => task.TaskID !== selectedTask.TaskID)
+ );
+ setShowDeleteModal(false); // Close modal after successful deletion
+ } catch (error) {
+ console.error("Error:", error);
+ setError("Something went wrong while deleting the task.");
+ }
+ }
+ };
+
+ const openDeleteModal = (task) => {
+ setSelectedTask(task);
+ setShowDeleteModal(true);
+ };
+
+ const closeDeleteModal = () => {
+ setShowDeleteModal(false);
+ setSelectedTask(null);
+ };
+
+ const handleViewTask = (taskID, category) => {
+ setSelectedTask({ taskID, category });
+ setShowModal(true);
+ };
+
return (
-
-
List of Assigned Tasks
-
+
+
+
+
+
+ Assigned Task List
+
+
+
+
+ {error && (
+
+ {error}
+
+ )}
+
+
# |
Title |
- Assignee |
Due Date |
Category |
- Status |
+ Description |
Actions |
- {tasks.map((task, index) => (
-
- {index + 1} |
- {task.title} |
- {task.assignee} |
- {task.dueDate} |
- {task.category} |
- {task.status} |
-
-
+ {tasks.length > 0 ? (
+ tasks.map((task, index) => {
+ console.log("Task object:", task); // Debugging log
+ return (
+ |
+ {index + 1} |
+ {task.Title} |
+ {task.DueDate ? new Date(task.DueDate).toLocaleDateString() : "No due date"} |
+ {task.Category || "N/A"} |
+ {task.Description || "N/A"} |
+
+
+
+
+ |
+
+ );
+ })
+ ) : (
+
+
+ No tasks available
|
- ))}
+ )}
-
+ {selectedTask && (
+ setShowModal(false)}
+ taskID={selectedTask.taskID}
+ category={selectedTask.category}
+ />
+ )}
+
+ {/* Delete Confirmation Modal */}
+
+
+ Confirm Deletion
+
+
+ Are you sure you want to delete the task{" "}
+ {selectedTask?.Title}?
+
+
+
+
+
+
+
);
};
diff --git a/src/components/UserDetails.jsx b/src/components/UserDetails.jsx
new file mode 100644
index 0000000..576bca1
--- /dev/null
+++ b/src/components/UserDetails.jsx
@@ -0,0 +1,112 @@
+import React, { useEffect, useState } from "react";
+import { useParams, useNavigate } from "react-router-dom";
+import { Container, Card, Row, Col, Button, Spinner, Alert } from "react-bootstrap";
+
+const ReadUser = () => {
+ const { id, firstname, lastname } = useParams();
+ const navigate = useNavigate();
+ const [user, setUser] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchReadUser = async () => {
+ try {
+ console.log(`Fetching User: ID=${id}, FirstName=${firstname}, LastName=${lastname}`);
+
+ const response = await fetch(`https://localhost:44346/api/users/details/${id}/${firstname}/${lastname}`);
+
+ if (!response.ok) {
+ throw new Error("Failed to fetch user details");
+ }
+
+ const data = await response.json();
+ console.log("API Response Data:", data); // Debugging API response
+
+ if (!Array.isArray(data) || data.length === 0) {
+ throw new Error("User not found or empty response.");
+ }
+
+ // Extract the first user from the array
+ setUser(data[0]);
+ } catch (error) {
+ console.error("Fetch Error:", error);
+ setError(error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchReadUser();
+ }, [id, firstname, lastname]);
+
+ useEffect(() => {
+ console.log("Updated User State:", user);
+ }, [user]);
+
+ if (loading) {
+ return (
+
+
+ Loading user details...
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+ Error: {error}
+
+
+ );
+ }
+
+ if (!user) {
+ return (
+
+
+ No user details found.
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ {user?.FirstName ?? "N/A"} {user?.LastName ?? "N/A"}
+ {user?.Email ?? "N/A"}
+
+
+ Middle Name: {user?.MiddleName ?? "N/A"}
+
+
+ Contact No: {user?.ContactNo ?? "N/A"}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ReadUser;
diff --git a/src/components/UserList.jsx b/src/components/UserList.jsx
index fdb36b9..3b7f6dc 100644
--- a/src/components/UserList.jsx
+++ b/src/components/UserList.jsx
@@ -1,32 +1,48 @@
import React, { useEffect, useState } from "react";
-import { Table, Button, Container } from "react-bootstrap";
+import { Table, Button, Container, Col, Row, Modal } from "react-bootstrap"; // Added Modal
+import { useNavigate } from "react-router-dom";
const UserList = () => {
const [users, setUsers] = useState([]);
+ const [showDeleteModal, setShowDeleteModal] = useState(false); // Modal state
+ const [selectedUser, setSelectedUser] = useState(null); // Selected user for deletion
+ const navigate = useNavigate();
+ // Fetch users only once on mount
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
try {
- const response = await fetch("https://your-api-url/api/users");
+ const response = await fetch(
+ "https://localhost:44346/api/users/informations"
+ );
+ if (!response.ok) throw new Error("Failed to fetch users");
+
const data = await response.json();
+ console.log("Fetched users:", data);
setUsers(data);
} catch (error) {
console.error("Error fetching users:", error);
}
};
- const handleDelete = async (id) => {
- if (window.confirm("Are you sure you want to delete this user?")) {
+ const handleDelete = async () => {
+ if (selectedUser) {
try {
- const response = await fetch(`https://your-api-url/api/user/${id}`, {
- method: "DELETE",
- });
+ const response = await fetch(
+ `https://localhost:44346/api/users/${selectedUser.UserID}/${selectedUser.FirstName}/${selectedUser.LastName}`,
+ {
+ method: "DELETE",
+ }
+ );
if (response.ok) {
- setUsers(users.filter((user) => user.id !== id));
+ setUsers((prevUsers) =>
+ prevUsers.filter((user) => user.UserID !== selectedUser.UserID)
+ );
+ setShowDeleteModal(false); // Close modal after successful deletion
} else {
alert("Error deleting user!");
}
@@ -36,9 +52,34 @@ const UserList = () => {
}
};
+ const openDeleteModal = (user) => {
+ setSelectedUser(user);
+ setShowDeleteModal(true);
+ };
+
+ const closeDeleteModal = () => {
+ setShowDeleteModal(false);
+ setSelectedUser(null);
+ };
+
return (
- List of Employers
+ {/* Enhanced Page Header */}
+
+
+
+
+ List of Employees
+
+
+
+
+ {/* User Table */}
@@ -52,23 +93,74 @@ const UserList = () => {
- {users.map((user, index) => (
-
- {index + 1} |
- {user.firstName} |
- {user.middleName} |
- {user.lastName} |
- {user.email} |
- {user.contactNo} |
-
-
-
-
+ {users.length > 0 ? (
+ users.map((user, index) => (
+ |
+ {index + 1} |
+ {user.FirstName} |
+ {user.MiddleName} |
+ {user.LastName} |
+ {user.Email} |
+ {user.ContactNo} |
+
+
+
+
+
+ |
+
+ ))
+ ) : (
+
+
+ No data available
|
- ))}
+ )}
+
+ {/* Delete Confirmation Modal */}
+
+
+ Confirm Deletion
+
+
+ Are you sure you want to delete{" "}
+
+ {selectedUser?.FirstName} {selectedUser?.LastName}
+
+ ?
+
+
+
+
+
+
);
};
diff --git a/src/css/AssignTasksMonitoring.css b/src/css/AssignTasksMonitoring.css
new file mode 100644
index 0000000..5f915db
--- /dev/null
+++ b/src/css/AssignTasksMonitoring.css
@@ -0,0 +1,54 @@
+.modern-table th, .modern-table td {
+ padding: 12px;
+ text-align: center;
+ }
+
+ .modern-table thead {
+ background-color: #007bff;
+ color: white;
+ font-weight: bold;
+ }
+
+ .modern-table tbody tr:nth-child(even) {
+ background-color: #f8f9fa;
+ }
+
+ .modern-table tbody tr:hover {
+ background-color: #e9ecef;
+ cursor: pointer;
+ }
+
+ .modern-table .form-select {
+ width: 100%;
+ }
+
+ .page-header .breadcrumb {
+ background: none;
+ padding: 0;
+ margin-bottom: 1rem;
+ font-size: 1rem;
+ }
+
+ .page-header .breadcrumb a {
+ color: #007bff;
+ text-decoration: none;
+ }
+
+ .page-header .breadcrumb a:hover {
+ text-decoration: underline;
+ }
+
+ .dashboard-title {
+ font-size: 2rem;
+ font-weight: bold;
+ }
+
+ button {
+ transition: background-color 0.3s ease, transform 0.3s ease;
+ }
+
+ button:hover {
+ background-color: #0056b3;
+ transform: translateY(-2px);
+ }
+
\ No newline at end of file