Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
71bab45
Prepare backend and frontend for final project deployment
phenomenalCode Sep 7, 2025
05598ef
prod ready lets go
phenomenalCode Sep 11, 2025
65c7ca0
prod ready lets gox2 i installed bycrypt
phenomenalCode Sep 11, 2025
da06c9a
deploy
phenomenalCode Sep 11, 2025
b29e226
deployx2
phenomenalCode Sep 11, 2025
8cf47cc
deployx3
phenomenalCode Sep 11, 2025
2338fe5
deployx4 group route fix
phenomenalCode Sep 14, 2025
bc62a20
deployx4 group route fix even better
phenomenalCode Sep 14, 2025
20e01c7
deployx5 robots file
phenomenalCode Sep 14, 2025
aa07701
Update README.md
phenomenalCode Sep 14, 2025
06a46a1
Removed console logs for security
phenomenalCode Oct 16, 2025
5aeb1f1
Merge branch 'main' of https://github.com/phenomenalCode/project-fina…
phenomenalCode Oct 16, 2025
eeeb81b
mobile-friendly GroupsManagement component with responsive layout
phenomenalCode Dec 3, 2025
29b3156
mobile-friendly GroupsManagement component with responsive layoutx2
phenomenalCode Dec 3, 2025
bf13a33
mobile-friendly GroupsManagement component with responsive layoutx2
phenomenalCode Dec 3, 2025
2e26d26
mobile-friendly GroupsManagement component with responsive layoutx3
phenomenalCode Dec 3, 2025
7e04677
mobile-friendly GroupsManagement component with responsive layoutx4 …
phenomenalCode Dec 3, 2025
b701c18
header fix
phenomenalCode Dec 3, 2025
109d299
header fix
phenomenalCode Dec 3, 2025
1df0897
restore
phenomenalCode Dec 3, 2025
8522302
sticky appbar
phenomenalCode Dec 3, 2025
c4db92f
overflow fixed
phenomenalCode Dec 3, 2025
10019c5
back to static
phenomenalCode Dec 3, 2025
442de58
Revise README for Task and Collaboration Management System
phenomenalCode Apr 11, 2026
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
File renamed without changes.
34 changes: 27 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
node_modules
.DS_Store
# Node modules
node_modules/
backend/node_modules/
frontend/node_modules/

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
backend/.env
frontend/.env

build
# Build outputs
frontend/dist/
frontend/build/
build/

npm-debug.log*
yarn-debug.log*
yarn-error.log*
# OS files
.DS_Store
Thumbs.db

# IDE configs
.vscode/
.idea/

package-lock.json
# Package lock
package-lock.json
139 changes: 132 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,138 @@
# Final Project
Task and Collaboration Management System
Overview
Live Demo

Replace this readme with your own information about your project.
Backend: https://project-final-darius-1.onrender.com

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
Frontend: https://project-final-darius.netlify.app/

## The problem
This project is a full-stack task and project management application designed to help users organize their work while supporting collaboration within groups. It combines task tracking, project organization, and shared group functionality into a single platform.

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
The system addresses a common problem: managing tasks individually is already challenging, but coordinating work across multiple users introduces additional complexity. This application provides a structured solution for both personal productivity and team-based workflows.

## View it live
Features
Task and Project Management
Create, update, and delete tasks
Mark tasks as completed
Organize tasks into projects
Automatic project progress calculation based on task completion
Filtering, search, and pagination for scalability

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
Group Collaboration

Create, join, and leave groups
Assign projects to groups
View group members
Shared workflows for collaborative productivity
Basic access control (only the creator can delete a group)

File and Folder Upload Workflow

Upload multiple files and entire folders when creating tasks
Files are sent using multipart FormData and processed with Multer
Supports scalable file storage (including GridFS integration)
Basic file type detection (images, PDFs, documents, text)
Preserves folder structure information for better organization

User Experience and Accessibility
Responsive design using Material-UI
Dark and light mode support
Accessible UI components (ARIA roles, dialogs, alerts)
Real-time UI updates through state-driven rendering
Integrated task creation flow combining projects, files, and deadlines

Tech Stack
Frontend
React (Vite)
Zustand (modular state management with persistence)
Material-UI
Axios
Backend
Node.js with Express
MongoDB
GridFS
Multer
JWT authentication
Deployment
Frontend hosted on Netlify
Backend hosted on Render
Architecture
Backend

The backend is built using Express and follows a modular and scalable structure with clear separation of routes, middleware, and models.

Backend Implementation Details
Environment configuration using dotenv for secure variable management
MongoDB connection initialized at server startup via a dedicated function
Custom CORS configuration restricting access to approved frontend origins
Middleware layer handling:
JSON and URL parsing
Authentication
Debug logging
Route structure:
/auth – authentication
/tasks – task management (protected)
/groups – collaboration system
Authentication and Authorization

A centralized authentication middleware verifies JWT tokens on protected routes. Once validated, user data is attached to the request object, enabling secure and consistent access control across the application.

File Handling

File uploads are processed using Multer, supporting multipart requests and integration with scalable storage solutions such as GridFS. Uploaded files can be classified by type (image, PDF, document, text), allowing for future extensibility.

Frontend
Modular State Management with Zustand

The application uses Zustand to manage global state in a modular way, separating logic across:

user authentication
group collaboration
task and project management

This structure keeps business logic independent from UI components and improves maintainability and scalability.

Persistent Authentication Flow

User authentication is persisted using JWT tokens stored in localStorage. On application load, the system restores the session and automatically attaches the token to API requests.

API-backed and Local State Synchronization
Backend-managed data (users, groups, files) is handled through API calls
Task and project data are persisted locally for fast access and improved UX
Axios is used for API communication with automatic authentication headers

Security

JWT-based authentication
Protected API routes using centralized middleware
Token verification with user context attached to requests
Authorization checks (e.g., only group creators can delete groups)
Input validation and structured error handling

Challenges

Designing consistent collaboration logic for group membership, ownership, and shared projects
Synchronizing frontend state with backend data while maintaining performance
Implementing scalable file and folder upload workflows
Structuring frontend logic using modular Zustand stores without overcomplication

Key Highlights

Full task CRUD with completion tracking
Project management with automatic progress calculation
Group-based collaboration system
File and folder upload workflow integrated into task creation
Centralized and modular state management using Zustand
Persistent authentication and session handling
Responsive and accessible UI design

What This Project Demonstrates

Ability to design and build a complete full-stack application
Strong understanding of modular frontend state management
Experience implementing persistent authentication flows using JWT
Knowledge of secure backend design using centralized middleware
Ability to handle multipart file uploads and classify uploaded content
Understanding of API-driven and client-side state synchronization
Focus on accessibility, usability, and responsive design
Experience building collaborative systems with shared user state
20 changes: 20 additions & 0 deletions backend/db/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import mongoose from "mongoose";

export const connectDB = async () => {
const mongoUrl = process.env.MONGO_URI || "mongodb://localhost:27017/task-manager";

try {
await mongoose.connect(mongoUrl, {
useNewUrlParser: true,

});
console.log("✅ MongoDB connected:", mongoUrl);
} catch (err) {
console.error("❌ MongoDB connection error:", err);
process.exit(1); // exit process if DB connection fails
}

mongoose.connection.on("disconnected", () => {
console.warn("⚠️ MongoDB disconnected!");
});
};
36 changes: 36 additions & 0 deletions backend/middleware/authmiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import jwt from "jsonwebtoken";

export const authMiddleware = (req, res, next) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader) {
console.warn("[Auth] No Authorization header");
return res.status(401).json({ error: "No token provided" });
}

const token = authHeader.split(" ")[1];
if (!token) {
console.warn("[Auth] No token after Bearer");
return res.status(401).json({ error: "No token provided" });
}

const decoded = jwt.verify(token, process.env.JWT_SECRET || "supersecret123");

if (!decoded) {
console.warn("[Auth] Token could not be decoded");
return res.status(401).json({ error: "Invalid token" });
}

// Ensure req.user has id and groupId
req.user = {
id: decoded.id,
groupId: decoded.groupId,
};


next();
} catch (err) {
console.error("[Auth] Error verifying token:", err.message);
res.status(401).json({ error: "Unauthorized" });
}
};
26 changes: 26 additions & 0 deletions backend/middleware/upload-task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import multer from "multer";
import path from "path";

// Store uploads in /uploads folder
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename: (req, file, cb) => {
cb(null, Date.now() + path.extname(file.originalname)); // unique name
},
});

export const upload = multer({ storage });

export const detectFileType = (mimetype) => {
if (mimetype.startsWith("image/")) return "image";
if (mimetype === "application/pdf") return "pdf";
if (
mimetype === "application/msword" ||
mimetype.includes("officedocument")
)
return "doc";
if (mimetype.startsWith("text/")) return "text";
return "other";
};
17 changes: 17 additions & 0 deletions backend/model/files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import mongoose from "mongoose";
const fileSchema = new mongoose.Schema({
name: String, // original file name
filename: String, // GridFS stored filename (with timestamp prefix)
url: String, // `/tasks/files/:filename` for frontend
contentType: String, // MIME type (e.g., text/plain, application/pdf)
size: Number, // file size in bytes
type: {
type: String,
enum: ["image", "pdf", "doc", "text", "other"],
default: "other"
},
folder: { type: String, default: "root" },
});


export default mongoose.model("File", fileSchema);
10 changes: 10 additions & 0 deletions backend/model/groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import mongoose from "mongoose";

const groupSchema = new mongoose.Schema({
name: { type: String, required: true },
members: [{ type: mongoose.Schema.Types.ObjectId, ref: "User" }],
currentProject: { type: String, default: null },
createdAt: { type: Date, default: Date.now },
});

export default mongoose.model("Group", groupSchema);
31 changes: 31 additions & 0 deletions backend/model/tasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import mongoose from "mongoose";

const fileSchema = new mongoose.Schema({
name: String, // original file name
filename: String, // GridFS stored filename (with timestamp prefix)
url: String, // `/tasks/files/:filename` for frontend
contentType: String, // MIME type (e.g., text/plain, application/pdf)
size: Number, // file size in bytes
type: {
type: String,
enum: ["image", "pdf", "doc", "text", "other"],
default: "other"
},
folder: { type: String, default: "root" },
});


const taskSchema = new mongoose.Schema({
title: { type: String, required: true, trim: true },
description: { type: String },
category: { type: String, enum: ["Work","Home","Health","Errands","Leisure","Other",""], default: "Other" } ,
priority: { type: String, enum: ["low", "medium", "high"], default: "medium" },
dueDate: { type: Date },
completed: { type: Boolean, default: false },
files: [fileSchema], // structured files
createdBy: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
group: { type: mongoose.Schema.Types.ObjectId, ref: "Group", required: true },
createdAt: { type: Date, default: Date.now },
});

export const Task = mongoose.model("Task", taskSchema);
11 changes: 11 additions & 0 deletions backend/model/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mongoose from "mongoose";

const userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true, trim: true },
email: { type: String, lowercase: true } ,// remove required
passwordHash: { type: String, required: true },
group: { type: mongoose.Schema.Types.ObjectId, ref: "Group" },
createdAt: { type: Date, default: Date.now }
});

export default mongoose.model("User", userSchema);
11 changes: 8 additions & 3 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
{
"name": "project-final-backend",
"version": "1.0.0",
"type": "module",
"description": "Server part of final project",
"scripts": {
"start": "babel-node server.js",
"dev": "nodemon server.js --exec babel-node"
"start": "babel-node backend/server.js",
"dev": "nodemon backend/server.js --exec babel-node"
},
"author": "",
"license": "ISC",
"dependencies": {
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"backend": "file:..",
"bcrypt": "^6.0.0",
"cors": "^2.8.5",
"express": "^4.17.3",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.4.0",
"multer": "^2.0.2",
"nodemon": "^3.0.1"
}
}
}
Loading