From 4835a2582b9840eb27013b5f9a37bd9fd1056e5b Mon Sep 17 00:00:00 2001 From: Aryan Das <122392506+aryandas2911@users.noreply.github.com> Date: Sun, 11 Jan 2026 13:23:09 +0530 Subject: [PATCH 001/169] Added README.md file --- README.md | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..a6126f63 --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +# DailyForge + +**DailyForge** is a fullstack MERN app to design and manage your weekly routines. Build routines with drag-and-drop tasks, save them, and visualize your schedule on an interactive dashboard. + +**Frontend:** [https://dailyforge-frontend-lhjq.onrender.com](https://dailyforge-frontend-lhjq.onrender.com) +**Backend:** [https://dailyforge-backend.onrender.com](https://dailyforge-backend.onrender.com) + +--- + +## Features + +- User Authentication (Signup/Login) with JWT +- CRUD operations for Tasks +- CRUD operations for Routines +- Weekly planner with drag-and-drop task placement +- Routine templates for quick reuse +- Interactive dashboard to view routines and tasks +- Overlap protection for tasks in the same day + +--- + +## Tech Stack + +**Frontend:** +- React +- Tailwind CSS +- Context API +- Axios + +**Backend:** +- Node.js +- Express.js +- MongoDB (Atlas) +- Mongoose ODM +- JWT for authentication +- Bcrypt for password hashing + +--- + +## Folder Structure + +```bash + backend/ + ├─ controllers/ + │ ├─ authController.js + │ ├─ routineController.js + │ ├─ taskController.js + ├─ models/ + │ ├─ User.model.js + │ ├─ Task.model.js + │ ├─ Routine.model.js + ├─ routes/ + │ ├─ authRoutes.js + │ ├─ taskRoutes.js + │ ├─ routineRoutes.js + ├─ middlewares/ + │ └─ authMiddleware.js + └─ server.js + + frontend/ + ├─ components/ + │ ├─ Dashboard/ + │ ├─ RoutineBuilder/ + │ ├─ TaskLibrary/ + ├─ context/ + │ └─ AuthContext.jsx + ├─ hooks/ + │ └─ useTasks.js + ├─ pages/ + │ ├─ Dashboard.jsx + │ ├─ RoutineBuilder.jsx + │ ├─ Tasks.jsx + │ └─ Auth/ + │ ├─ Login.jsx + │ └─ Signup.jsx + ├─ api/ + │ └─ axiosConfig.js + └─ App.jsx +``` + +--- + +## Installation + +### **Backend** +```bash + git clone https://github.com/aryandas2911/DailyForge.git + cd backend + npm install + npm run dev +``` + +### **Frontend** +```bash + cd frontend + npm install + npm start +``` + +--- + +## Usage + +1. Signup/Login +2. Create tasks with title, duration, color, and category +3. Open Routine Builder to drag and drop tasks into the weekly grid +4. Save routines and view them in the Dashboard +5. Update or delete tasks and routines as needed + +--- + +## Screenshots + +### Signup Page: +![Signup Page](Screenshots/Signup.png) + +### Dashboard Page: +![Dashboard](Screenshots/Dashboard.png) + +### Tasks Page: +![Tasks page](Screenshots/Tasks.png) + +### Routine Page: +![Routine page](Screenshots/Routine.png) + +--- + +## Contributing + +Contributions are welcome! + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature-name`) +3. Commit your changes (`git commit -m 'Add feature'`) +4. Push to the branch (`git push origin feature-name`) +5. Open a Pull Request From 46cf7326aee7af6ce358acd15a68cd227d66b7f5 Mon Sep 17 00:00:00 2001 From: Aryan Das <122392506+aryandas2911@users.noreply.github.com> Date: Sun, 10 May 2026 12:54:59 +0530 Subject: [PATCH 002/169] Update npm start command to npm run dev --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6126f63..d6f74f4e 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ ```bash cd frontend npm install - npm start + npm run dev ``` --- From a5044086f71c515239b8a81bbaa998350e901415 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 18:56:16 +0530 Subject: [PATCH 003/169] Updated README.md --- README.md | 415 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 323 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index a6126f63..fd1113c3 100644 --- a/README.md +++ b/README.md @@ -1,136 +1,367 @@ -# DailyForge +
-**DailyForge** is a fullstack MERN app to design and manage your weekly routines. Build routines with drag-and-drop tasks, save them, and visualize your schedule on an interactive dashboard. +# 🔨 DailyForge -**Frontend:** [https://dailyforge-frontend-lhjq.onrender.com](https://dailyforge-frontend-lhjq.onrender.com) -**Backend:** [https://dailyforge-backend.onrender.com](https://dailyforge-backend.onrender.com) +### Build routines. Forge habits. Own your week. + +**DailyForge** is an open-source fullstack MERN productivity app that lets you design, manage, and visualize your weekly routines — with drag-and-drop scheduling, a smart task library, and overlap protection built right in. + +[![GSSoC](https://img.shields.io/badge/GSSoC-2026-orange?style=for-the-badge)](https://gssoc.girlscript.tech/) +[![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-brightgreen?style=for-the-badge)](CONTRIBUTING.md) +[![Stars](https://img.shields.io/github/stars/aryandas2911/DailyForge?style=for-the-badge)](https://github.com/aryandas2911/DailyForge/stargazers) + +[🌐 Live Demo](#-live-demo) · [⚡ Quick Start](#-quick-start) · [🤝 Contribute](#-contribution-guidelines) · [📸 Screenshots](#-screenshots) + +
--- -## Features +## 🚀 Project Overview + +Most productivity tools are either too bloated or too simple. **DailyForge** is a no-nonsense weekly planner that gives you total control over your schedule — built by students, for students and professionals alike. + +**What it does:** +- Build a reusable **task library** with custom durations, colors, and categories +- Design **weekly routines** by dragging tasks into a visual time grid +- Save, update, and delete **routines** with one click +- Automatically detects and prevents **scheduling conflicts** for the same day -- User Authentication (Signup/Login) with JWT -- CRUD operations for Tasks -- CRUD operations for Routines -- Weekly planner with drag-and-drop task placement -- Routine templates for quick reuse -- Interactive dashboard to view routines and tasks -- Overlap protection for tasks in the same day +**Why it matters:** +Most people don't fail to plan — they fail to stick to a plan. DailyForge makes routines feel visual and deliberate, making habits easier to build and track. + +**Key highlights:** +- ⚡ Drag-and-drop weekly planner powered by `@dnd-kit` +- 🔒 Secure JWT authentication with bcrypt password hashing +- 🗂️ Reusable routine templates to clone and reuse schedules +- 🚫 Conflict detection — no overlapping tasks on the same day +- 📱 Clean, responsive UI built with React 19 + Tailwind CSS v4 --- -## Tech Stack +## 🌐 Live Demo -**Frontend:** -- React -- Tailwind CSS -- Context API -- Axios +| Service | URL | +|----------|-----| +| 🖥️ Frontend | [https://dailyforge-frontend-lhjq.onrender.com](https://dailyforge-frontend-lhjq.onrender.com) | +| ⚙️ Backend API | [https://dailyforge-backend.onrender.com](https://dailyforge-backend.onrender.com) | -**Backend:** -- Node.js -- Express.js -- MongoDB (Atlas) -- Mongoose ODM -- JWT for authentication -- Bcrypt for password hashing +> ⚠️ Deployed on Render's free tier — first load may take 30–60 seconds to spin up. --- -## Folder Structure +## ✨ Features + +### 🔐 Authentication +- Signup / Login with JWT-based session management +- Protected routes — unauthenticated users are redirected to login +- Passwords hashed with bcrypt + +### 📋 Task Management +- Create tasks with: title, duration, color, and category +- Edit and delete tasks from your personal task library +- Tasks persist across sessions + +### 🗓️ Routine Builder +- Drag tasks from your library onto a **7-day weekly grid** +- Time-slot-based placement with visual feedback +- Overlap detection prevents conflicting task placement on the same day + +### 📊 Dashboard +- View all saved routines at a glance +- Quick access to edit or delete any routine +- Summary stats for your weekly schedule + +### ♻️ Routine Templates +- Save any routine as a reusable template +- Re-apply templates to any week in seconds + +--- + +## 🏗 Tech Stack + +### Frontend +| Technology | Purpose | +|------------|---------| +| React 19 | UI framework | +| Vite | Build tool & dev server | +| Tailwind CSS v4 | Utility-first styling | +| `@dnd-kit/core` | Drag-and-drop interactions | +| Axios | HTTP client for API calls | +| React Router DOM v7 | Client-side routing | +| Lucide React | Icon library | +| Context API | Global auth state management | + +### Backend +| Technology | Purpose | +|------------|---------| +| Node.js | Runtime environment | +| Express.js v5 | REST API framework | +| MongoDB Atlas | Cloud database | +| Mongoose v9 | ODM for MongoDB | +| JSON Web Token (JWT) | Stateless authentication | +| Bcrypt | Password hashing | +| dotenv | Environment variable management | +| Nodemon | Dev server with hot-reload | + +--- + +## 📂 Project Structure + +``` +DailyForge/ +│ +├── backend/ +│ ├── config/ # DB connection config +│ ├── controllers/ +│ │ ├── authController.js # Signup, login logic +│ │ ├── routineController.js +│ │ └── taskController.js +│ ├── middlewares/ +│ │ └── authMiddleware.js # JWT verification +│ ├── routes/ +│ │ ├── authRoutes.js +│ │ ├── routineRoutes.js +│ │ └── taskRoutes.js +│ ├── src/ +│ │ ├── models/ # Mongoose schemas +│ │ │ ├── User.model.js +│ │ │ ├── Task.model.js +│ │ │ └── Routine.model.js +│ │ └── server.js # Express app entry point +│ ├── .env # ← You create this (see below) +│ └── package.json +│ +└── frontend/ + ├── public/ + ├── src/ + │ ├── api/ + │ │ └── axiosConfig.js # Axios base URL config + │ ├── components/ + │ │ ├── Dashboard/ + │ │ ├── Routine/ + │ │ ├── Task/ + │ │ ├── Navbar.jsx + │ │ └── ProtectedRoutes.jsx + │ ├── context/ + │ │ └── AuthContext.jsx + │ ├── hooks/ + │ │ └── useTasks.js + │ ├── pages/ + │ │ ├── Dashboard.jsx + │ │ ├── RoutineBuilder.jsx + │ │ ├── Tasks.jsx + │ │ ├── Login.jsx + │ │ └── Signup.jsx + │ ├── utils/ + │ ├── App.jsx + │ └── main.jsx + ├── index.html + ├── vite.config.js + └── package.json +``` + +--- + +## ⚡ Quick Start + +**Prerequisites:** Node.js v18+, npm v9+, a free [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) account + +### 1. Clone the repository + +```bash +git clone https://github.com/aryandas2911/DailyForge.git +cd DailyForge +``` + +--- + +### 2. Set up the Backend ```bash - backend/ - ├─ controllers/ - │ ├─ authController.js - │ ├─ routineController.js - │ ├─ taskController.js - ├─ models/ - │ ├─ User.model.js - │ ├─ Task.model.js - │ ├─ Routine.model.js - ├─ routes/ - │ ├─ authRoutes.js - │ ├─ taskRoutes.js - │ ├─ routineRoutes.js - ├─ middlewares/ - │ └─ authMiddleware.js - └─ server.js - - frontend/ - ├─ components/ - │ ├─ Dashboard/ - │ ├─ RoutineBuilder/ - │ ├─ TaskLibrary/ - ├─ context/ - │ └─ AuthContext.jsx - ├─ hooks/ - │ └─ useTasks.js - ├─ pages/ - │ ├─ Dashboard.jsx - │ ├─ RoutineBuilder.jsx - │ ├─ Tasks.jsx - │ └─ Auth/ - │ ├─ Login.jsx - │ └─ Signup.jsx - ├─ api/ - │ └─ axiosConfig.js - └─ App.jsx +cd backend +npm install ``` +**Create your `.env` file** (see the [Environment Variables](#-environment-variables) section below): + +```bash +# Inside the /backend directory +cp .env.example .env # or manually create .env +``` + +Then fill in your values (see the next section for what each variable means). + +**Start the backend dev server:** + +```bash +npm run dev +``` + +> ✅ Server should start at `http://localhost:5000` + --- -## Installation +### 3. Set up the Frontend + +Open a **new terminal**, then: -### **Backend** ```bash - git clone https://github.com/aryandas2911/DailyForge.git - cd backend - npm install - npm run dev +cd frontend +npm install ``` -### **Frontend** +> ⚠️ **Local dev note:** The frontend base URL is hardcoded in `frontend/src/api/axios.js` to point at the deployed backend. To test against your local backend, temporarily change `baseURL` in that file to `http://localhost:5000/api/`. + +**Start the frontend dev server:** + ```bash - cd frontend - npm install - npm start +npm run dev ``` +> ✅ App should open at `http://localhost:5173` + --- -## Usage +### ✅ You're ready! -1. Signup/Login -2. Create tasks with title, duration, color, and category -3. Open Routine Builder to drag and drop tasks into the weekly grid -4. Save routines and view them in the Dashboard -5. Update or delete tasks and routines as needed +Open `http://localhost:5173`, sign up for an account, and start building your routines. --- -## Screenshots +## 🔐 Environment Variables + +### Backend — `backend/.env` + +Create this file manually. **Never commit it to git.** + +```env +PORT=5000 +MONGO_URI=your_mongodb_atlas_connection_string +JWT_SECRET=your_super_secret_key_here +``` + +| Variable | Required | Description | +|----------|----------|-------------| +| `PORT` | ✅ | Port on which the Express server runs (default: `5000`) | +| `MONGO_URI` | ✅ | Full MongoDB Atlas connection string — get it from your Atlas cluster's "Connect" menu | +| `JWT_SECRET` | ✅ | Secret key for signing JWTs — use any long, random string (e.g., `openssl rand -hex 32`) | + +**How to get `MONGO_URI`:** +1. Log into [MongoDB Atlas](https://cloud.mongodb.com) +2. Create a free M0 cluster (if you haven't) +3. Click **Connect** → **Connect your application** → Copy the connection string +4. Replace `` with your DB user's password -### Signup Page: +### Frontend — No `.env` required + +The frontend has **no environment variables**. The API base URL is hardcoded in `frontend/src/api/axios.js`: + +```js +// frontend/src/api/axios.js +const api = axios.create({ + baseURL: "https://dailyforge-backend.onrender.com/api/", +}); +``` + +**Running locally?** Change `baseURL` to `http://localhost:5000/api/` while developing, and revert before committing. + +--- + +## 🤝 Contribution Guidelines + +We love contributions! DailyForge is actively participating in **GSSoC 2026** and welcomes contributors of all experience levels. + +📄 **Read the full guidelines:** [CONTRIBUTING.md](CONTRIBUTING.md) + +### Quick Contribution Flow + +**1. Pick an issue** +- Browse [open issues](https://github.com/aryandas2911/DailyForge/issues) +- Look for `good first issue` if you're new +- Comment on the issue to get it assigned before starting work + +**2. Fork & branch** +```bash +git clone https://github.com/aryandas2911/DailyForge.git +cd DailyForge +git checkout -b / +``` + +**Branch naming convention:** + +| Type | Example | +|------|---------| +| New feature | `feature/add-dark-mode` | +| Bug fix | `fix/login-redirect-loop` | +| Documentation | `docs/update-readme` | +| Refactor | `refactor/task-hook-cleanup` | + +**3. Make your changes** +- Keep changes focused — one issue per PR +- Follow the existing code style +- Test your changes locally before pushing + +**4. Open a Pull Request** +- Fill out the PR template completely +- Link the issue it resolves using `Closes #` +- Request a review from a maintainer + +> ⚠️ PRs without a linked issue or description will not be reviewed. + +--- + +## 🏷 Issue Guidelines + +We use labels to organize work. Here's what they mean: + +| Label | Meaning | +|-------|---------| +| `good first issue` | Small, well-scoped tasks — perfect for first-time contributors | +| `bug` | Something is broken or behaving incorrectly | +| `feature` | New functionality to be added | +| `help wanted` | Issues where maintainer input or extra hands are needed | +| `documentation` | Improvements to README, guides, or inline comments | +| `testing` | Adding or improving test coverage | + +**Tips for new contributors:** +- Start with `good first issue` — they're designed to be approachable +- Don't hesitate to ask questions in the issue comments +- One issue at a time — don't take on multiple issues until your first PR is merged + +--- + +## 📸 Screenshots + +### 🔐 Signup Page ![Signup Page](Screenshots/Signup.png) -### Dashboard Page: +### 📊 Dashboard ![Dashboard](Screenshots/Dashboard.png) -### Tasks Page: -![Tasks page](Screenshots/Tasks.png) +### 📋 Tasks Page +![Tasks Page](Screenshots/Tasks.png) -### Routine Page: -![Routine page](Screenshots/Routine.png) +### 🗓️ Routine Builder +![Routine Builder](Screenshots/Routine.png) --- -## Contributing -Contributions are welcome! +## 📬 Contact & Community + +Have questions, ideas, or want to connect with other contributors? + +| Channel | Link | +|---------|------| +| 📧 Email | aryandas2911@gmail.com | +| 🐛 Issues | [GitHub Issues](https://github.com/aryandas2911/DailyForge/issues) | + +--- + +
+ +**Built with ❤️ for GSSoC 2026** + +If DailyForge helped you, consider giving it a ⭐ — it helps more contributors find the project! -1. Fork the repository -2. Create a feature branch (`git checkout -b feature-name`) -3. Commit your changes (`git commit -m 'Add feature'`) -4. Push to the branch (`git push origin feature-name`) -5. Open a Pull Request +
From 739ee37c1b795adfa37591e7d5b0e96f0166a9a5 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 18:59:25 +0530 Subject: [PATCH 004/169] Updated README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index fd1113c3..0cb4de45 100644 --- a/README.md +++ b/README.md @@ -319,9 +319,7 @@ We use labels to organize work. Here's what they mean: | `good first issue` | Small, well-scoped tasks — perfect for first-time contributors | | `bug` | Something is broken or behaving incorrectly | | `feature` | New functionality to be added | -| `help wanted` | Issues where maintainer input or extra hands are needed | | `documentation` | Improvements to README, guides, or inline comments | -| `testing` | Adding or improving test coverage | **Tips for new contributors:** - Start with `good first issue` — they're designed to be approachable From 2acfd93f79ad94358c77eadc145c17fefdda9e1f Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 19:10:18 +0530 Subject: [PATCH 005/169] CONTRIBUTING.md added --- CONTRIBUTING.md | 320 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..d9b208f0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,320 @@ +# 🤝 Contributing to DailyForge + +> **DailyForge** is an active open-source project participating in **GSSoC 2026**. +> This document is not a suggestion — it is the standard. All contributions must comply with it. +> Read it fully before writing a single line of code. + +--- + +## 🚀 Getting Started + +**Before anything else:** + +1. Read the [README.md](README.md) completely — it covers setup, project structure, and environment variables. +2. Make sure your local environment is fully working before picking an issue. +3. If setup fails, check existing [GitHub Issues](https://github.com/aryandas2911/DailyForge/issues) before opening a new one. + +**Finding issues to work on:** + +- Browse [open issues](https://github.com/aryandas2911/DailyForge/issues) +- **New contributors must start with `good first issue` labeled issues** — do not jump to `feature` or complex `bug` issues on your first contribution +- Filter by label to find the right issue for your skill level +- Read the issue description fully before commenting + +--- + +## 📌 Issue Assignment Rules + +These rules exist to keep the project moving efficiently. They are strictly enforced. + +- **One issue per contributor at a time.** Do not request a second issue until your first PR is merged. +- **You must comment on the issue and wait for assignment** before starting any work. Unassigned PRs will be closed. +- **Do not open a PR for an issue assigned to someone else.** +- **Inactivity policy:** If you are assigned an issue and show no progress (no commits, no updates) within **3 days**, you will be automatically unassigned and the issue will be reopened. No exceptions. +- If you need more time, comment on the issue before the deadline — not after. + +> ⚠️ PRs submitted for issues you were not assigned to will be **closed immediately** without review. + +--- + +## 🌿 Branch Naming Convention + +All branches must follow this naming convention. PRs from non-conforming branch names will be asked to rename. + +``` +/ +``` + +| Type | When to use | Example | +|------|------------|---------| +| `feature` | Adding new functionality | `feature/dark-mode-toggle` | +| `fix` | Fixing a bug | `fix/login-redirect-loop` | +| `docs` | Documentation changes only | `docs/update-contributing` | +| `refactor` | Code restructuring, no behavior change | `refactor/task-hook-cleanup` | +| `style` | CSS/UI-only changes, no logic | `style/navbar-spacing` | +| `test` | Adding or fixing tests | `test/auth-controller-unit` | + +**Rules:** +- Always branch off from `main` (or the branch specified in the issue) +- Keep branch names lowercase and hyphen-separated +- No generic names like `patch-1`, `my-fix`, or `update` + +```bash +# Example +git checkout main +git pull origin main +git checkout -b fix/routine-overlap-detection +``` + +--- + +## 💬 Commit Message Guidelines + +DailyForge follows the **[Conventional Commits](https://www.conventionalcommits.org/)** specification. Every commit message must follow this format: + +``` +: +``` + +**Allowed types:** + +| Type | Purpose | +|------|---------| +| `feat` | New feature or functionality | +| `fix` | Bug fix | +| `docs` | Documentation changes | +| `refactor` | Code restructure without behavior change | +| `style` | Formatting, CSS, no logic change | +| `chore` | Build config, dependencies, tooling | + +**Real examples:** + +``` +feat: add overlap detection for same-day tasks +fix: resolve JWT token expiry not clearing localStorage +docs: update quick start steps in README +refactor: extract task validation logic into utility function +style: align routine cards in dashboard grid view +``` + +**Rules:** +- Use **present tense**, imperative mood — *"add"* not *"added"* or *"adds"* +- Keep the description under 72 characters +- Do not end with a period +- Do not use vague messages like `"fixed stuff"`, `"update"`, or `"changes"` + +> ❌ `git commit -m "update"` → Rejected +> ✅ `git commit -m "fix: prevent duplicate routine save on double-click"` → Accepted + +--- + +## 🔧 Development Workflow + +Follow this workflow **exactly**. Skipping steps leads to rejected PRs. + +``` +Fork → Clone → Branch → Code → Test Locally → Push → Open PR +``` + +### Step-by-step + +**1. Fork the repository** +Click **Fork** on [github.com/aryandas2911/DailyForge](https://github.com/aryandas2911/DailyForge) + +**2. Clone your fork** +```bash +git clone https://github.com//DailyForge.git +cd DailyForge +``` + +**3. Create a branch** +```bash +git checkout -b / +``` + +**4. Make your changes** +- Follow the project structure — do not create files in arbitrary locations +- Do not install new dependencies without discussing in the issue first +- Do not modify files unrelated to your issue + +**5. Test locally — mandatory** +- Run the full app (backend + frontend) and verify your change works +- Run `npm run lint` in the `frontend/` directory and fix all errors +- Confirm no existing features are broken + +**6. Commit and push** +```bash +git add . +git commit -m "feat: your meaningful message here" +git push origin +``` + +**7. Open a Pull Request** +Open a PR from your fork to `main` on the original repo. + +--- + +## 📥 Pull Request Guidelines + +**A PR that does not meet these requirements will be closed without review.** + +### Every PR must include: + +- ✅ **Clear description** of what was changed and why +- ✅ **Linked issue** using `Closes #` in the PR description +- ✅ **Screenshots or screen recordings** if the change affects the UI in any way +- ✅ **Local test confirmation** — state that you ran the app and tested your change +- ✅ **Focused scope** — one issue, one PR. Do not bundle unrelated changes + +## 🔗 PR Template + +**You must use the PR template.** PRs submitted without it will be rejected. + +When you open a PR, the template will be auto-populated. Fill out every section: + +```markdown +## Description + + +## Related Issue +Closes # + +## Changes Made + +- +- + +## Screenshots (if UI changes) + + +## Checklist +- [ ] I have read CONTRIBUTING.md +- [ ] My branch follows the naming convention +- [ ] I have tested this locally +- [ ] Linting passes (`npm run lint` in frontend/) +- [ ] No unrelated files were modified +- [ ] Screenshots are attached (if UI change) +``` + +> PRs with empty descriptions, unchecked checklists, or missing issue links will be **closed immediately**. + +--- + +## 🔍 Code Review Process + +- Maintainers review PRs **line-by-line** — code quality is taken seriously +- You may be asked to **explain your logic** for a specific implementation decision +- Respond to all review comments before requesting a re-review +- Do not resolve review comments yourself unless explicitly told to +- Do not merge your own PR + +**A PR may be rejected if:** +- The logic is unclear or overly complex without justification +- Code quality is below the existing standard +- React best practices are not followed (unnecessary re-renders, improper state management, etc.) +- Backend routes are not protected with `authMiddleware` where required +- Hardcoded values appear where they shouldn't (secrets, magic numbers, URLs) + +--- + +## ⏱ Review Timeline + +- PRs are typically reviewed within **24–48 hours** of submission +- Reviews may take longer during high-activity periods (GSSoC peak weeks) +- **Do not ping maintainers repeatedly.** One reminder after 48 hours is acceptable. Repeated unnecessary pings will be noted. +- If your PR has been waiting more than 3 days, leave a single polite comment on the PR — do not DM or open duplicate issues + +--- + +## 🎨 Code Style Guidelines + +### General +- Follow the existing code patterns — look at similar files before creating new ones +- Keep functions small and single-purpose +- Name variables and functions descriptively — `handleTaskDelete` not `htd` +- No magic numbers — use named constants + +### Frontend (React) +- Use functional components only — no class components +- Manage state with `useState` / `useContext` / custom hooks — no prop drilling more than 2 levels +- Place reusable logic in `src/hooks/` or `src/utils/` +- New components go in `src/components/` under the appropriate subfolder +- New pages go in `src/pages/` +- Do not write inline styles — use Tailwind CSS classes + +### Backend (Node/Express) +- All business logic goes in `controllers/` — keep routes thin +- All protected routes must use `authMiddleware` +- Use `async/await` with proper `try/catch` blocks — no unhandled promise rejections +- Mongoose models go in `src/models/` +- Return consistent JSON response shapes: `{ success, message, data }` + +### Security +- **Never hardcode secrets, API keys, or credentials** — use `backend/.env` +- Never commit `.env` files +- Validate and sanitize all user inputs on the backend + +--- + +## ❌ What Will Get Your PR Rejected + +No ambiguity here. Your PR **will be closed** if: + +| Violation | Result | +|-----------|--------| +| No linked issue | Closed immediately | +| PR not following the PR template | Closed immediately | +| Issue was not assigned to you | Closed immediately | +| Copied code without understanding | Closed with feedback | +| No explanation of logic when asked | Closed after no response in 48h | +| Breaking existing functionality | Closed, must fix and resubmit | +| Unrelated files modified | Closed, must clean up | +| Ignoring review feedback | Closed after no response in 72h | +| Adding unnecessary dependencies | Closed with feedback | +| Hardcoded secrets or API keys | Closed immediately | + +--- + +## 🏷 Labels Guide + +| Label | Meaning | Who should pick it | +|-------|---------|-------------------| +| `good first issue` | Small, well-scoped, documented — ideal for first-time contributors | First-time contributors | +| `bug` | Confirmed broken behavior | Intermediate contributors | +| `feature` | New functionality — may require architecture decisions | Experienced contributors | +| `documentation` | README, guides, inline comments | Anyone | + +**New contributors:** Do not skip `good first issue`. It exists for a reason — it's where you prove you understand the workflow before taking on harder tasks. + +--- + +## 🔥 Enforcement Policy + +These rules are **actively enforced**. This is not boilerplate. + +- **First low-quality PR:** Closed with feedback and a link back to this document +- **Second violation of the same rule:** Closed with minimal comment +- **Pattern of low-effort contributions or bad-faith behavior:** Contributor will be disengaged from the project +- Maintainers reserve the right to close any PR that does not meet the standards described here, without detailed explanation + +If you receive feedback, act on it. If you disagree, discuss it professionally in the PR comments. + +--- + +## 🤝 Code of Conduct + +- Treat all contributors and maintainers with respect +- Disagreements are resolved through discussion, not dismissiveness +- No spam in issues or PRs (e.g., `+1` comments, empty reactions fishing for attention) +- Constructive feedback only — critique the code, not the person +- Harassment, discrimination, or bad-faith behavior of any kind will result in immediate removal + +--- + +
+ +**Questions?** Open a [GitHub Discussion](https://github.com/aryandas2911/DailyForge/discussions) or reach out via [aryandas2911@gmail.com](mailto:aryandas2911@gmail.com) + +*Built with ❤️ for GSSoC 2026 — DailyForge* + +
diff --git a/README.md b/README.md index 0cb4de45..6b217ea3 100644 --- a/README.md +++ b/README.md @@ -282,7 +282,7 @@ We love contributions! DailyForge is actively participating in **GSSoC 2026** an **2. Fork & branch** ```bash -git clone https://github.com/aryandas2911/DailyForge.git +git clone https://github.com//DailyForge.git cd DailyForge git checkout -b / ``` From 3e3235a690bdd87bba62e83927766fd78bafd8c4 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 19:11:51 +0530 Subject: [PATCH 006/169] Updated CONTRIBUTING.md --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9b208f0..d11cf7b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -313,8 +313,6 @@ If you receive feedback, act on it. If you disagree, discuss it professionally i
-**Questions?** Open a [GitHub Discussion](https://github.com/aryandas2911/DailyForge/discussions) or reach out via [aryandas2911@gmail.com](mailto:aryandas2911@gmail.com) - *Built with ❤️ for GSSoC 2026 — DailyForge*
From 935ed059a38a8155ecea3d25f8ae5219da82fd76 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 19:14:12 +0530 Subject: [PATCH 007/169] Updated README.md and CONTRIBUTING.md --- CONTRIBUTING.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d11cf7b9..51688327 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -283,6 +283,8 @@ No ambiguity here. Your PR **will be closed** if: | `bug` | Confirmed broken behavior | Intermediate contributors | | `feature` | New functionality — may require architecture decisions | Experienced contributors | | `documentation` | README, guides, inline comments | Anyone | +| `help wanted` | Maintainers need external input or hands | Experienced contributors | +| `testing` | Adding or improving test coverage | Anyone comfortable with testing | **New contributors:** Do not skip `good first issue`. It exists for a reason — it's where you prove you understand the workflow before taking on harder tasks. diff --git a/README.md b/README.md index 6b217ea3..091d3a9f 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,8 @@ We use labels to organize work. Here's what they mean: | `bug` | Something is broken or behaving incorrectly | | `feature` | New functionality to be added | | `documentation` | Improvements to README, guides, or inline comments | +| `help wanted` | Maintainers need external input or hands | Experienced contributors | +| `testing` | Adding or improving test coverage | Anyone comfortable with testing | **Tips for new contributors:** - Start with `good first issue` — they're designed to be approachable From 448a2c865e23ba4e5c33dd0477f64bfadb6d0745 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 20:23:13 +0530 Subject: [PATCH 008/169] Added pull request template --- .github/pull_request_template.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..aed071da --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,23 @@ +## 📌 Description +Briefly explain the changes made. + +## 🔗 Related Issue +Closes # + +## 🛠 Changes Made +- +- +- + +## 📸 Screenshots (if applicable) +Add screenshots or GIFs to explain UI changes. + +## ✅ Checklist +- [ ] Code runs locally +- [ ] Followed project structure +- [ ] No console errors +- [ ] Properly tested changes +- [ ] Linked the issue + +## 🚀 Notes for Reviewers +Anything specific you want reviewed. \ No newline at end of file From 08caa63263bd00b40b4e10b29af2802ee70f4742 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 20:53:39 +0530 Subject: [PATCH 009/169] Added CI Checks --- .github/workflows/ci.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..51beb7ef --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI Checks + +on: + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + + # 🔹 Backend Install + - name: Install Backend Dependencies + run: | + cd backend + npm install + + # 🔹 Frontend Install + - name: Install Frontend Dependencies + run: | + cd frontend + npm install + + # 🔹 Build Frontend + - name: Build Frontend + run: | + cd frontend + npm run build \ No newline at end of file From 2753d78e2fcf95c456f3ed2546d8a5d2285b7f58 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 21:07:45 +0530 Subject: [PATCH 010/169] Linting enabled --- backend/eslint.config.js | 20 + backend/package-lock.json | 883 ++++++++++++++++++++++++++++++++++++++ backend/package.json | 8 +- 3 files changed, 910 insertions(+), 1 deletion(-) create mode 100644 backend/eslint.config.js diff --git a/backend/eslint.config.js b/backend/eslint.config.js new file mode 100644 index 00000000..909fd5ed --- /dev/null +++ b/backend/eslint.config.js @@ -0,0 +1,20 @@ +import js from "@eslint/js"; +import globals from "globals"; + +export default [ + js.configs.recommended, + { + files: ["**/*.js"], + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: { + ...globals.node, + }, + }, + rules: { + "no-unused-vars": "warn", + "no-console": "off", + }, + }, +]; \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index 9c9b2e7f..867ea976 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -17,6 +17,244 @@ "mongodb": "^7.0.0", "mongoose": "^9.0.0", "nodemon": "^3.1.11" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "eslint": "^10.3.0", + "globals": "^17.6.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@mongodb-js/saslprep": { @@ -28,6 +266,27 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -56,6 +315,46 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -283,6 +582,21 @@ "node": ">= 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -300,6 +614,13 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -395,6 +716,223 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -447,6 +985,40 @@ "url": "https://opencollective.com/express" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -480,6 +1052,44 @@ "url": "https://opencollective.com/express" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -570,6 +1180,19 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -651,12 +1274,32 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "license": "ISC" }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -720,6 +1363,34 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", @@ -772,6 +1443,46 @@ "node": ">=18.0.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -991,6 +1702,13 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -1099,6 +1817,56 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1108,6 +1876,26 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", @@ -1130,6 +1918,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1306,6 +2104,29 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -1468,6 +2289,19 @@ "node": ">=18" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -1497,6 +2331,16 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1528,11 +2372,50 @@ "node": ">=18" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/backend/package.json b/backend/package.json index b81768ea..2683ca55 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "dev": "nodemon src/server.js" + "dev": "nodemon src/server.js", + "lint": "eslint ." }, "keywords": [], "author": "", @@ -20,5 +21,10 @@ "mongodb": "^7.0.0", "mongoose": "^9.0.0", "nodemon": "^3.1.11" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "eslint": "^10.3.0", + "globals": "^17.6.0" } } From 6c67d49aacff65ec5b950f90c16d9aee8a858edc Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 21:09:18 +0530 Subject: [PATCH 011/169] CI pipeline optimized --- .github/workflows/ci.yml | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51beb7ef..946d8ecc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,35 +2,52 @@ name: CI Checks on: pull_request: - branches: [ "main" ] + branches: ["main"] + push: + branches: ["main"] jobs: - build: + ci: runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18] + steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 18 + node-version: ${{ matrix.node-version }} + cache: "npm" - # 🔹 Backend Install - name: Install Backend Dependencies - run: | - cd backend - npm install + working-directory: backend + run: npm ci + + - name: Lint Backend + working-directory: backend + run: npm run lint - # 🔹 Frontend Install - name: Install Frontend Dependencies - run: | - cd frontend - npm install + working-directory: frontend + run: npm ci + + - name: Lint Frontend + working-directory: frontend + run: npm run lint + + - name: Backend Tests + working-directory: backend + run: npm test --if-present + + - name: Frontend Tests + working-directory: frontend + run: npm test --if-present - # 🔹 Build Frontend - name: Build Frontend - run: | - cd frontend - npm run build \ No newline at end of file + working-directory: frontend + run: npm run build \ No newline at end of file From ca71421b2a92364aae78ae235165a6833cc27df9 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 21:13:11 +0530 Subject: [PATCH 012/169] Updated ci.yml --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 946d8ecc..8e646f7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,9 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: "npm" + cache-dependency-path: | + backend/package-lock.json + frontend/package-lock.json - name: Install Backend Dependencies working-directory: backend From 5baa3c4c4bc2e2151a62cb04b293277a590a8e57 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 21:26:16 +0530 Subject: [PATCH 013/169] Fixed backend linting errors --- backend/config/db.js | 2 +- backend/controllers/authController.js | 2 +- backend/eslint.config.js | 2 +- backend/src/server.js | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/config/db.js b/backend/config/db.js index 7b997163..11e6f0ff 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -5,7 +5,7 @@ const connectDB = async () => { try { await mongoose.connect(process.env.MONGO_URI); console.log("Connection to MongoDB successful"); - } catch (error) { + } catch (_error) { console.log("Error connecting to MongoDB"); } }; diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js index abfde47f..eb79839d 100644 --- a/backend/controllers/authController.js +++ b/backend/controllers/authController.js @@ -89,7 +89,7 @@ export const getUser = async (req, res) => { .json({ success: false, message: "User not found" }); } return res.status(200).json({ success: true, user: user }); - } catch (error) { + } catch (_error) { // error handling return res .status(500) diff --git a/backend/eslint.config.js b/backend/eslint.config.js index 909fd5ed..b33b563b 100644 --- a/backend/eslint.config.js +++ b/backend/eslint.config.js @@ -13,7 +13,7 @@ export default [ }, }, rules: { - "no-unused-vars": "warn", + "no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_" }], "no-console": "off", }, }, diff --git a/backend/src/server.js b/backend/src/server.js index 414e9726..3fe0d27a 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -9,8 +9,6 @@ import { routineRouter } from "../routes/routineRoutes.js"; // dotenv config dotenv.config(); const PORT = process.env.PORT; -const MONGO_URI = process.env.MONGO_URI; -const JWT_SECRET = process.env.JWT_SECRET; // Initialize express app const app = express(); From 531929d22732b35cc0f2bb497c095f25b265be74 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 21:34:04 +0530 Subject: [PATCH 014/169] Frontend linting errors fixed --- frontend/src/components/Task/TaskFormModal.jsx | 2 ++ frontend/src/context/AuthContext.jsx | 15 ++++++++------- frontend/src/hooks/useTasks.js | 5 +++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/Task/TaskFormModal.jsx b/frontend/src/components/Task/TaskFormModal.jsx index 679176e3..f3c60647 100644 --- a/frontend/src/components/Task/TaskFormModal.jsx +++ b/frontend/src/components/Task/TaskFormModal.jsx @@ -12,11 +12,13 @@ export default function TaskFormModal({ task, onClose, onSubmit }) { useEffect(() => { if (task) { + /* eslint-disable react-hooks/set-state-in-effect */ setTitle(task.title || ""); setDescription(task.description || ""); setTags(task.tags || ""); setPriority(task.priority || "Low"); setDueDate(task.dueDate ? task.dueDate.split("T")[0] : ""); + /* eslint-enable react-hooks/set-state-in-effect */ } }, [task]); diff --git a/frontend/src/context/AuthContext.jsx b/frontend/src/context/AuthContext.jsx index 5ae5469c..aad5c665 100644 --- a/frontend/src/context/AuthContext.jsx +++ b/frontend/src/context/AuthContext.jsx @@ -2,6 +2,7 @@ import { createContext, useEffect, useState } from "react"; import api from "../api/axios"; // create context component +// eslint-disable-next-line react-refresh/only-export-components export const AuthContext = createContext(null); // provider component @@ -9,6 +10,13 @@ const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [token, setToken] = useState(() => localStorage.getItem("token")); + // logout function + const logout = () => { + setUser(null); + setToken(null); + localStorage.removeItem("token"); + }; + // restore session on app load useEffect(() => { if (token) { @@ -25,13 +33,6 @@ const AuthProvider = ({ children }) => { } }, [token]); - // logout function - const logout = () => { - setUser(null); - setToken(null); - localStorage.removeItem("token"); - }; - return ( {children} diff --git a/frontend/src/hooks/useTasks.js b/frontend/src/hooks/useTasks.js index ce2cbb79..2497bf32 100644 --- a/frontend/src/hooks/useTasks.js +++ b/frontend/src/hooks/useTasks.js @@ -16,13 +16,13 @@ const useTasks = () => { // create new task const addTask = async (taskData) => { - const res = await api.post("/tasks", taskData); + await api.post("/tasks", taskData); getTasks(); }; // update task const updateTask = async (id, updates) => { - const res = await api.put(`/tasks/${id}`, updates); + await api.put(`/tasks/${id}`, updates); getTasks(); }; @@ -34,6 +34,7 @@ const useTasks = () => { // initial fetch useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect getTasks(); }, []); From 343355b95ddc74b7f9445f60e070050e7ee93b57 Mon Sep 17 00:00:00 2001 From: Aryan Das Date: Sun, 10 May 2026 21:37:40 +0530 Subject: [PATCH 015/169] Backend linting error fixed --- backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 2683ca55..23c97dab 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "echo \"No tests specified yet\"", "dev": "nodemon src/server.js", "lint": "eslint ." }, From c8e66092fa9565657e503a1603e9a905a09e3f74 Mon Sep 17 00:00:00 2001 From: Aryan Das <122392506+aryandas2911@users.noreply.github.com> Date: Tue, 12 May 2026 20:15:54 +0530 Subject: [PATCH 016/169] Added LICENSE.md file --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d0015ec8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Aryan Das + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 63ce5de677e62422c5f6182c37b805034a3cd473 Mon Sep 17 00:00:00 2001 From: rajat552 Date: Fri, 15 May 2026 12:43:31 +0530 Subject: [PATCH 017/169] fix: resolve missing key prop warning in Dashboard (#35) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- frontend/src/pages/Dashboard.jsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index e13e2ce1..385f6fc0 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -132,10 +132,7 @@ export default function Dashboard() { ) : (
    {savedRoutines.map((routine) => ( -
  • +
  • {routine.name}

    {routine.items.length} tasks across{" "} From 1b9b67765d60e8a67e72cc135404a60030ded1dc Mon Sep 17 00:00:00 2001 From: Aditya Sekhar Das <144005527+AdityaSekharDas@users.noreply.github.com> Date: Fri, 15 May 2026 12:50:38 +0530 Subject: [PATCH 018/169] fix: remove redundant useTasks call in TaskPreview (#47) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- frontend/src/components/Dashboard/TaskPreview.jsx | 5 ++--- frontend/src/pages/Dashboard.jsx | 7 +++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Dashboard/TaskPreview.jsx b/frontend/src/components/Dashboard/TaskPreview.jsx index 89f5da12..c8cc3aba 100644 --- a/frontend/src/components/Dashboard/TaskPreview.jsx +++ b/frontend/src/components/Dashboard/TaskPreview.jsx @@ -1,9 +1,8 @@ import { useNavigate } from "react-router-dom"; -import useTasks from "../../hooks/useTasks"; -export default function TaskPreview({ tasks }) { + +export default function TaskPreview({ tasks , updateTask}) { const navigate = useNavigate(); - const { updateTask } = useTasks(); const priorityBorder = { Low: "border-green-400", diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 385f6fc0..dcc70d2e 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -16,7 +16,7 @@ export default function Dashboard() { const [savedRoutines, setSavedRoutines] = useState([]); const [loadingRoutines, setLoadingRoutines] = useState(false); - const { tasks } = useTasks(); + const { tasks, updateTask } = useTasks(); const today = new Date(); @@ -106,7 +106,10 @@ export default function Dashboard() {

    {/* Upcoming Tasks */}
    - +
    {/* Saved Routines */} From 0a83b2760b4fbd3617be03393284891af443742f Mon Sep 17 00:00:00 2001 From: Babin Bid <144826118+KGFCH2@users.noreply.github.com> Date: Fri, 15 May 2026 12:55:20 +0530 Subject: [PATCH 019/169] docs: add security policy (#187) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- SECURITY.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..627091d5 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,32 @@ +# Security Policy + +## Supported Versions + +Currently, the following versions of DailyForge are supported with security updates: + +| Version | Supported | +| ------- | ------------------ | +| v1.0.x | :white_check_mark: | +| < v1.0 | :x: | + +## Reporting a Vulnerability + +We take the security of DailyForge seriously. If you believe you have found a security vulnerability, please report it to us as soon as possible. + +**Please do not open a public GitHub issue for security vulnerabilities.** + +Instead, please send an email to: **aryandas2911@gmail.com** + +In your report, please include: +- A descriptive title for the vulnerability. +- A detailed description of the vulnerability. +- Steps to reproduce the issue (PoC). +- Potential impact of the vulnerability. + +### What to Expect + +- You will receive an acknowledgment of your report within **48 hours**. +- We will provide a timeline for the fix and keep you updated on the progress. +- Once the vulnerability is resolved, we will provide credit to the reporter (if desired) in our release notes. + +Thank you for helping keep DailyForge secure! From 8f84db2c99800f0d761f4919fc0f23a0238b2558 Mon Sep 17 00:00:00 2001 From: Ankit Verma Date: Fri, 15 May 2026 13:19:29 +0530 Subject: [PATCH 020/169] docs: add localhost CORS setup instructions (#165) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 091d3a9f..c4996a94 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,23 @@ cp .env.example .env # or manually create .env ``` Then fill in your values (see the next section for what each variable means). +> ⚠️ **Local dev note:** The backend CORS origin is currently configured for the deployed frontend in `backend/src/server.js`. +> When running the frontend locally on `http://localhost:5173`, update the CORS origin temporarily for local development. +> +> Change this: +> +> ```js +> origin: "https://dailyforge-frontend-lhjq.onrender.com" +> ``` +> +> to: +> +> ```js +> origin: "http://localhost:5173" +> ``` +> +> before starting the backend server. + **Start the backend dev server:** From 759a3c8ae8f187d25ef7ec9c41716d049150826d Mon Sep 17 00:00:00 2001 From: Harish S S <165870545+Harish-SS56@users.noreply.github.com> Date: Fri, 15 May 2026 13:45:22 +0530 Subject: [PATCH 021/169] docs: add standardized JSDoc comments for all utility functions in frontend/src/utils (#143) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- frontend/src/utils/constants.js | 74 ++++++++++++++ frontend/src/utils/dateUtils.js | 109 ++++++++++++++++++++ frontend/src/utils/index.js | 14 +++ frontend/src/utils/taskUtils.js | 142 ++++++++++++++++++++++++++ frontend/src/utils/timeUtils.js | 79 ++++++++++++++ frontend/src/utils/validationUtils.js | 64 ++++++++++++ 6 files changed, 482 insertions(+) create mode 100644 frontend/src/utils/constants.js create mode 100644 frontend/src/utils/dateUtils.js create mode 100644 frontend/src/utils/index.js create mode 100644 frontend/src/utils/taskUtils.js create mode 100644 frontend/src/utils/timeUtils.js create mode 100644 frontend/src/utils/validationUtils.js diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js new file mode 100644 index 00000000..751f6fc0 --- /dev/null +++ b/frontend/src/utils/constants.js @@ -0,0 +1,74 @@ +/** + * @module constants + * @description Shared constants used across DailyForge frontend components. + */ + +/** + * Ordered days of the week starting Monday. + * @type {Array} + */ +export const DAYS_OF_WEEK = [ + "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday", +]; + +/** + * Task priority levels in ascending urgency. + * @type {Array} + */ +export const PRIORITIES = ["Low", "Medium", "High"]; + +/** + * Numeric weight per priority for sorting (higher = more urgent). + * @type {Object} + */ +export const PRIORITY_ORDER = { High: 3, Medium: 2, Low: 1 }; + +/** + * Tailwind border-color classes per priority level. + * @type {Object} + */ +export const PRIORITY_BORDER_STYLES = { + Low: "border-green-400", + Medium: "border-yellow-400", + High: "border-red-500", +}; + +/** + * Tailwind badge classes (bg + text) per priority level. + * @type {Object} + */ +export const PRIORITY_BADGE_STYLES = { + Low: "bg-green-100 text-green-700", + Medium: "bg-yellow-100 text-yellow-700", + High: "bg-red-100 text-red-700", +}; + +/** + * Tailwind card styles (border + bg) per priority level. + * @type {Object} + */ +export const PRIORITY_CARD_STYLES = { + Low: "border-green-500 bg-green-50", + Medium: "border-yellow-500 bg-yellow-50", + High: "border-red-500 bg-red-50", +}; + +/** + * Hex color codes per priority level for dot indicators. + * @type {Object} + */ +export const PRIORITY_COLORS = { + High: "#ef4444", + Medium: "#f59e0b", + Low: "#10b981", +}; + +/** + * Task status enum values. + * @type {Object} + */ +export const TASK_STATUS = { + DUE: "Due", + COMPLETED: "Completed", +}; diff --git a/frontend/src/utils/dateUtils.js b/frontend/src/utils/dateUtils.js new file mode 100644 index 00000000..b4d48c7d --- /dev/null +++ b/frontend/src/utils/dateUtils.js @@ -0,0 +1,109 @@ +/** + * @module dateUtils + * @description Utility functions for date operations used across the DailyForge application. + * Provides helpers for comparing, formatting, and computing date ranges. + */ + +/** + * Checks whether two Date objects fall on the same calendar day + * (same year, month, and date). + * + * @param {Date} date1 - The first date to compare. + * @param {Date} date2 - The second date to compare. + * @returns {boolean} `true` if both dates share the same year, month, and day; otherwise `false`. + * + * @example + * isSameDay(new Date("2026-05-14"), new Date("2026-05-14")); // true + * isSameDay(new Date("2026-05-14"), new Date("2026-05-15")); // false + */ +export function isSameDay(date1, date2) { + return ( + date1.getFullYear() === date2.getFullYear() && + date1.getMonth() === date2.getMonth() && + date1.getDate() === date2.getDate() + ); +} + +/** + * Formats a Date object into a human-readable string + * such as "Wednesday · 14 May". + * + * @param {Date} date - The date to format. + * @returns {string} A formatted date string with weekday, day, and abbreviated month. + * + * @example + * formatDashboardDate(new Date("2026-05-14")); + * // "Wednesday · 14 May" + */ +export function formatDashboardDate(date) { + return date + .toLocaleDateString("en-US", { + weekday: "long", + day: "2-digit", + month: "short", + }) + .replace(",", " ·"); +} + +/** + * Formats an ISO date string (or Date object) into a short + * weekday abbreviation (e.g., "Mon", "Tue"). + * + * @param {string | Date} dateInput - An ISO date string or Date object. + * @returns {string} The short weekday name for the given date. + * + * @example + * formatShortWeekday("2026-05-14T00:00:00Z"); // "Wed" + */ +export function formatShortWeekday(dateInput) { + return new Date(dateInput).toLocaleDateString("en-US", { + weekday: "short", + }); +} + +/** + * Returns a new Date object set to `daysFromNow` days in the future + * relative to the current date and time. + * + * @param {number} daysFromNow - Number of days to add to the current date. + * @returns {Date} A Date object representing the future date. + * + * @example + * const threeDaysLater = getFutureDate(3); + */ +export function getFutureDate(daysFromNow) { + const date = new Date(); + date.setDate(date.getDate() + daysFromNow); + return date; +} + +/** + * Formats an ISO date string (or Date object) into a locale-aware + * date string using the browser's default locale settings. + * + * @param {string | Date} dateInput - An ISO date string or Date object. + * @returns {string} A locale-formatted date string (e.g., "5/14/2026" for en-US). + * + * @example + * formatLocalDate("2026-05-14T00:00:00Z"); // "5/14/2026" (en-US) + */ +export function formatLocalDate(dateInput) { + return new Date(dateInput).toLocaleDateString(); +} + +/** + * Extracts the date portion (YYYY-MM-DD) from an ISO 8601 date-time string. + * Useful for populating HTML date inputs from API responses. + * + * @param {string} isoString - An ISO 8601 date-time string (e.g., "2026-05-14T10:30:00.000Z"). + * @returns {string} The date portion of the string (e.g., "2026-05-14"), + * or an empty string if the input is falsy. + * + * @example + * extractDateFromISO("2026-05-14T10:30:00.000Z"); // "2026-05-14" + * extractDateFromISO(""); // "" + */ +export function extractDateFromISO(isoString) { + if (!isoString) return ""; + return isoString.split("T")[0]; +} diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js new file mode 100644 index 00000000..483d30bd --- /dev/null +++ b/frontend/src/utils/index.js @@ -0,0 +1,14 @@ +/** + * @module utils + * @description Central barrel file that re-exports all utility modules + * from the DailyForge utils directory. Import from here for convenience. + * + * @example + * import { isSameDay, filterTasksByDate, timeToMinutes } from "../utils"; + */ + +export * from "./dateUtils"; +export * from "./taskUtils"; +export * from "./timeUtils"; +export * from "./validationUtils"; +export * from "./constants"; diff --git a/frontend/src/utils/taskUtils.js b/frontend/src/utils/taskUtils.js new file mode 100644 index 00000000..8f56fda7 --- /dev/null +++ b/frontend/src/utils/taskUtils.js @@ -0,0 +1,142 @@ +/** + * @module taskUtils + * @description Utility functions for filtering, sorting, and computing + * statistics on task collections in the DailyForge application. + */ + +import { isSameDay } from "./dateUtils"; + +/** + * Filters a list of tasks to include only those created on a specific day. + * + * @param {Array} tasks - The full array of task objects. + * @param {Date} [targetDate=new Date()] - The target date to filter by (defaults to today). + * @returns {Array} Tasks whose `createdAt` falls on the same calendar day as `targetDate`. + * + * @example + * const todayTasks = filterTasksByDate(tasks); + * const yesterdayTasks = filterTasksByDate(tasks, new Date("2026-05-13")); + */ +export function filterTasksByDate(tasks, targetDate = new Date()) { + return tasks.filter((task) => { + const created = new Date(task.createdAt); + return isSameDay(targetDate, created); + }); +} + +/** + * Filters tasks to return only those that are not yet completed. + * + * @param {Array} tasks - The full array of task objects. + * @returns {Array} Tasks whose `status` is not "Completed". + * + * @example + * const pending = filterIncompleteTasks(tasks); + */ +export function filterIncompleteTasks(tasks) { + return tasks.filter((task) => task.status !== "Completed"); +} + +/** + * Filters tasks to return only completed tasks. + * + * @param {Array} tasks - The full array of task objects. + * @returns {Array} Tasks whose `status` is "Completed". + * + * @example + * const done = filterCompletedTasks(tasks); + */ +export function filterCompletedTasks(tasks) { + return tasks.filter((task) => task.status === "Completed"); +} + +/** + * Returns tasks that have a due date within a specified number of days + * from now and are not yet completed. + * + * @param {Array} tasks - The full array of task objects. + * @param {number} [withinDays=3] - Number of days from today to look ahead. + * @returns {Array} Incomplete tasks with a due date between now + * and `withinDays` days in the future (inclusive). + * + * @example + * const urgent = getUpcomingDeadlines(tasks, 3); + */ +export function getUpcomingDeadlines(tasks, withinDays = 3) { + const now = new Date(); + const futureDate = new Date(); + futureDate.setDate(now.getDate() + withinDays); + + return tasks.filter((task) => { + if (!task.dueDate || task.status === "Completed") return false; + const due = new Date(task.dueDate); + return due >= now && due <= futureDate; + }); +} + +/** + * Counts the number of incomplete tasks that have a specific priority level. + * + * @param {Array} tasks - The full array of task objects. + * @param {string} priority - The priority level to count (e.g., "High", "Medium", "Low"). + * @returns {number} The count of incomplete tasks matching the given priority. + * + * @example + * const highCount = countTasksByPriority(tasks, "High"); // 4 + */ +export function countTasksByPriority(tasks, priority) { + return tasks.filter( + (t) => t.priority === priority && t.status !== "Completed" + ).length; +} + +/** + * Calculates the completion percentage of a list of tasks. + * + * @param {Array} tasks - The full array of task objects. + * @returns {number} An integer between 0 and 100 representing the percentage + * of tasks that are completed. Returns 0 when the list is empty. + * + * @example + * getCompletionPercent([{ status: "Completed" }, { status: "Due" }]); // 50 + * getCompletionPercent([]); // 0 + */ +export function getCompletionPercent(tasks) { + const total = tasks.length; + if (!total) return 0; + const completed = tasks.filter((t) => t.status === "Completed").length; + return Math.round((completed / total) * 100); +} + +/** + * Sorts an array of tasks by their `createdAt` date in ascending + * (oldest-first) order. + * + * @param {Array} tasks - The array of task objects to sort. + * @returns {Array} A new array sorted by `createdAt` ascending. + * + * @example + * const sorted = sortTasksByCreatedDate(tasks); + */ +export function sortTasksByCreatedDate(tasks) { + return [...tasks].sort( + (a, b) => new Date(a.createdAt) - new Date(b.createdAt) + ); +} + +/** + * Sorts an array of tasks by priority in descending order + * (High → Medium → Low). + * + * @param {Array} tasks - The array of task objects to sort. + * @returns {Array} A new array sorted by priority, highest first. + * + * @example + * const sorted = sortTasksByPriority(tasks); // High tasks come first + */ +export function sortTasksByPriority(tasks) { + const priorityOrder = { High: 3, Medium: 2, Low: 1 }; + return [...tasks].sort( + (a, b) => priorityOrder[b.priority] - priorityOrder[a.priority] + ); +} diff --git a/frontend/src/utils/timeUtils.js b/frontend/src/utils/timeUtils.js new file mode 100644 index 00000000..3fa7a4fd --- /dev/null +++ b/frontend/src/utils/timeUtils.js @@ -0,0 +1,79 @@ +/** + * @module timeUtils + * @description Utility functions for time-related operations used primarily + * in the Routine Builder feature of DailyForge. Handles conversion between + * time strings and minute values, as well as time-slot generation. + */ + +/** + * Converts a time string in "HH:mm" format to the total number of + * minutes since midnight. + * + * @param {string} time - A time string in "HH:mm" format (e.g., "09:30"). + * @returns {number} The total minutes since midnight (e.g., 570 for "09:30"). + * + * @example + * timeToMinutes("09:30"); // 570 + * timeToMinutes("00:00"); // 0 + * timeToMinutes("23:59"); // 1439 + */ +export function timeToMinutes(time) { + const [h, m] = time.split(":").map(Number); + return h * 60 + m; +} + +/** + * Converts a total-minutes value back to a "HH:mm" formatted string. + * Hours and minutes are zero-padded to two digits. + * + * @param {number} totalMinutes - Total minutes since midnight (0–1439). + * @returns {string} A time string in "HH:mm" format. + * + * @example + * minutesToTime(570); // "09:30" + * minutesToTime(0); // "00:00" + * minutesToTime(1439); // "23:59" + */ +export function minutesToTime(totalMinutes) { + const hours = String(Math.floor(totalMinutes / 60)).padStart(2, "0"); + const minutes = String(totalMinutes % 60).padStart(2, "0"); + return `${hours}:${minutes}`; +} + +/** + * Generates an array of hourly time-slot strings from a start hour + * to an end hour (inclusive). + * + * @param {number} [startHour=6] - The first hour to include (0–23). + * @param {number} [endHour=22] - The last hour to include (0–23). + * @returns {Array} An array of "HH:00" time-slot strings. + * + * @example + * generateTimeSlots(); // ["06:00", "07:00", ..., "22:00"] + * generateTimeSlots(9, 17); // ["09:00", "10:00", ..., "17:00"] + */ +export function generateTimeSlots(startHour = 6, endHour = 22) { + const slots = []; + let hour = startHour; + while (hour <= endHour) { + slots.push(`${String(hour).padStart(2, "0")}:00`); + hour++; + } + return slots; +} + +/** + * Formats a start-time value (in minutes since midnight) into a + * human-readable "HH:mm" display string. Useful for rendering + * routine schedule entries. + * + * @param {number} startTimeMinutes - The start time expressed as minutes since midnight. + * @returns {string} The formatted time string in "HH:mm" format. + * + * @example + * formatStartTime(540); // "09:00" + * formatStartTime(810); // "13:30" + */ +export function formatStartTime(startTimeMinutes) { + return minutesToTime(startTimeMinutes); +} diff --git a/frontend/src/utils/validationUtils.js b/frontend/src/utils/validationUtils.js new file mode 100644 index 00000000..acdc10e7 --- /dev/null +++ b/frontend/src/utils/validationUtils.js @@ -0,0 +1,64 @@ +/** + * @module validationUtils + * @description Utility functions for validating user input in the + * DailyForge application. These helpers ensure that task form data + * and other user-submitted values meet the required criteria before + * being sent to the API. + */ + +/** + * Validates the required fields of a task form submission. + * Returns an error message string if validation fails, or `null` + * if all fields are valid. + * + * @param {Object} taskData - The task data to validate. + * @param {string} taskData.title - The title of the task. + * @param {string} taskData.priority - The priority level ("Low", "Medium", or "High"). + * @param {string} taskData.dueDate - The due date in "YYYY-MM-DD" format. + * @returns {string | null} An error message if validation fails, or `null` if valid. + * + * @example + * validateTaskForm({ title: "", priority: "Low", dueDate: "2026-05-14" }); + * // "Title is required" + * + * validateTaskForm({ title: "Study", priority: "High", dueDate: "2026-05-14" }); + * // null + */ +export function validateTaskForm({ title, priority, dueDate }) { + if (!title || !title.trim()) return "Title is required"; + if (!priority) return "Priority is required"; + if (!dueDate) return "Due date is required"; + return null; +} + +/** + * Checks whether a given string is a properly formatted email address. + * Uses a basic regular expression — suitable for client-side pre-validation. + * + * @param {string} email - The email string to validate. + * @returns {boolean} `true` if the string matches a standard email pattern; otherwise `false`. + * + * @example + * isValidEmail("user@example.com"); // true + * isValidEmail("invalid-email"); // false + * isValidEmail(""); // false + */ +export function isValidEmail(email) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +} + +/** + * Validates that a routine name is non-empty after trimming whitespace. + * + * @param {string} name - The routine name to validate. + * @returns {boolean} `true` if the trimmed name has at least one character; otherwise `false`. + * + * @example + * isValidRoutineName("Monday Routine"); // true + * isValidRoutineName(" "); // false + * isValidRoutineName(""); // false + */ +export function isValidRoutineName(name) { + return Boolean(name && name.trim()); +} From a800e6609264fbb0b51463e6824e5dfd0addca57 Mon Sep 17 00:00:00 2001 From: Pratikshya <2406043@kiit.ac.in> Date: Fri, 15 May 2026 13:51:17 +0530 Subject: [PATCH 022/169] fix: add JWT expiration to prevent non-expiring tokens (#125) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- backend/.env.example | 1 + backend/controllers/authController.js | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 backend/.env.example diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 00000000..3d638c40 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1 @@ +JWT_EXPIRES_IN=7d \ No newline at end of file diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js index eb79839d..e1466c0b 100644 --- a/backend/controllers/authController.js +++ b/backend/controllers/authController.js @@ -67,9 +67,11 @@ export const login = async (req, res) => { } // generate jwt token - const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { - expiresIn: "24h", - }); + const token = jwt.sign( + { id: user._id }, + process.env.JWT_SECRET, + { expiresIn: process.env.JWT_EXPIRES_IN || "7d" } +); return res.status(200).json({ message: "Login successful", token }); } catch (error) { // error handling From 1079620e29d61b6fb248c92bc69f55dbd3127b10 Mon Sep 17 00:00:00 2001 From: Ishta P Jain Date: Fri, 15 May 2026 13:58:28 +0530 Subject: [PATCH 023/169] fix: raise axios timeout and add user-friendly timeout error message (#63) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- frontend/src/api/axios.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/frontend/src/api/axios.js b/frontend/src/api/axios.js index 67e99ae7..53ce8ce5 100644 --- a/frontend/src/api/axios.js +++ b/frontend/src/api/axios.js @@ -3,7 +3,7 @@ import axios from "axios"; // create axios instance const api = axios.create({ baseURL: "https://dailyforge-backend.onrender.com/api/", - timeout: 2000, + timeout: Number(import.meta.env.VITE_API_TIMEOUT) || 15000, // updated 15s as default }); // attach jwt automatically with each request @@ -24,4 +24,22 @@ api.interceptors.request.use((config) => { } }); -export default api; +// Handle response errors, including timeout +api.interceptors.response.use( + (response) => response, // success — pass through unchanged + (error) => { + if (error.code === "ECONNABORTED") { + // This fires when the timeout is hit + console.error("Request timed out. The server may be waking up from sleep. Please wait a moment and try again."); + error.userMessage = + "The server is waking up — this can take up to 30 seconds on first load. Please try again shortly."; + } else if (!error.response) { + // Network error (no internet, server completely unreachable) + console.error("Network error. Please check your connection."); + error.userMessage = "Network error. Please check your internet connection."; + } + return Promise.reject(error); // always reject so callers can handle it + } +); + +export default api; \ No newline at end of file From 8dcbd0ce5e8c2805382f07ccb957fa6d77301442 Mon Sep 17 00:00:00 2001 From: Shreeiya Trivedi Date: Fri, 15 May 2026 14:54:55 +0530 Subject: [PATCH 024/169] feat: added optional description field to routines #62 (#104) Co-authored-by: Aryan Das <122392506+aryandas2911@users.noreply.github.com> --- backend/controllers/routineController.js | 11 ++++++----- backend/src/models/Routine.js | 4 ++++ frontend/src/pages/Dashboard.jsx | 7 ++++++- frontend/src/pages/RoutineBuilder.jsx | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/backend/controllers/routineController.js b/backend/controllers/routineController.js index 626ec5de..5e529b4d 100644 --- a/backend/controllers/routineController.js +++ b/backend/controllers/routineController.js @@ -14,7 +14,7 @@ export const createRoutine = async (req, res) => { } // fetch routine details from request body - const { name, items } = req.body; + const { name, description, items } = req.body; if (!name || items.length == 0 || !items) { return res .status(400) @@ -74,6 +74,7 @@ export const createRoutine = async (req, res) => { const newRoutine = new Routine({ userId, name, + description, items, }); @@ -111,7 +112,7 @@ export const getRoutines = async (req, res) => { createdAt: -1, }); if (routines.length == 0) { - res.status(400).json({ message: "User has no routine", success: false }); + return res.status(400).json({ message: "User has no routine", success: false }); } return res.status(200).json({ success: true, routines }); } catch (error) { @@ -192,7 +193,7 @@ export const updateRoutine = async (req, res) => { message: "Routine not found", }); } - res.status(200).json({ + return res.status(200).json({ message: "Routine updated successfully", routine: updatedRoutine, }); @@ -226,11 +227,11 @@ export const deleteRoutine = async (req, res) => { userId: userId, }); if (!deleteRoutine) { - res.status(404).json({ + return res.status(404).json({ message: "Routine not found", }); } - res.status(200).json({ + return res.status(200).json({ message: "Routine deleted successfully", }); } catch (error) { diff --git a/backend/src/models/Routine.js b/backend/src/models/Routine.js index 542bdcb3..7ea58c87 100644 --- a/backend/src/models/Routine.js +++ b/backend/src/models/Routine.js @@ -11,6 +11,10 @@ const routineSchema = mongoose.Schema( type: String, required: true, }, + description: { + type: String, + required: false, + }, items: [ // tasks { taskId: { diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index dcc70d2e..0ffc077e 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -137,7 +137,12 @@ export default function Dashboard() { {savedRoutines.map((routine) => (
  • {routine.name}

    -

    + {routine.description && ( +

    + {routine.description} +

    + )} +

    {routine.items.length} tasks across{" "} {new Set(routine.items.map((i) => i.day)).size} day(s)

    diff --git a/frontend/src/pages/RoutineBuilder.jsx b/frontend/src/pages/RoutineBuilder.jsx index 096ca294..f6d71df8 100644 --- a/frontend/src/pages/RoutineBuilder.jsx +++ b/frontend/src/pages/RoutineBuilder.jsx @@ -18,6 +18,7 @@ export default function RoutineBuilder() { const [routineName, setRoutineName] = useState(""); const [savedRoutines, setSavedRoutines] = useState([]); const [loadingRoutines, setLoadingRoutines] = useState(false); + const [description, setDescription] = useState(""); const handleSubmit = async (data) => { try { @@ -62,11 +63,13 @@ export default function RoutineBuilder() { try { await api.post("/routines", { name: routineName, + description: description, items, }); setIsSaveModalOpen(false); setRoutineName(""); + setDescription(""); setSelectedDay(null); alert("Routine saved successfully"); @@ -183,6 +186,12 @@ export default function RoutineBuilder() { {routine.name} + {routine.description && ( +

    + {routine.description} +

    + )} + {Object.keys(tasksByDay).map((day) => (

    {day}

    @@ -236,6 +245,14 @@ export default function RoutineBuilder() { className="w-full mb-4 rounded-xl border-soft px-3 py-2 text-sm focus:outline-none" /> +