A full-stack task tracking application built with Spring Boot 3.5 and vanilla HTML/CSS/JavaScript. Users can register, create projects, assign tasks to teammates, track progress, and receive email notifications.
- Technologies Used
- Setup Instructions
- Email Configuration
- Database Schema
- API Documentation
- Permission Model
- Frontend Features
- Assumptions Made
| Layer | Technology |
|---|---|
| Language | Java 17 |
| Framework | Spring Boot 3.5.0 |
| REST API | Spring Web (MVC) |
| Database | H2 In-Memory |
| ORM | Spring Data JPA / Hibernate |
| Migrations | Flyway |
| Validation | Spring Validation (Jakarta) |
| Security | Spring Security (HTTP Basic) |
| Spring Mail (Jakarta Mail / Gmail SMTP) | |
| Documentation | Swagger / SpringDoc OpenAPI |
| Testing | JUnit 5, Mockito, MockMvc, @DataJpaTest |
| Frontend | HTML, CSS, JavaScript (Fetch API) |
| Build | Maven |
- Java 17+
- Maven 3.8+
- A Gmail account with an App Password (for email notifications)
git clone https://github.com/piperoavi/TaskTracker.git
cd TaskTrackerOpen src/main/resources/application.yml and fill in:
spring:
mail:
username: your-gmail@gmail.com
password: "your-16-char-app-password"See Email Configuration for how to get a Gmail App Password.
Email is non-critical — if not configured, the app runs fine and tasks are created normally.
mvn spring-boot:runOr run TaskTrackerApplication.java directly from IntelliJ IDEA.
http://localhost:8080/index.html
| URL | Description |
|---|---|
http://localhost:8080/index.html |
Main frontend |
http://localhost:8080/swagger-ui/index.html |
Swagger API docs |
http://localhost:8080/h2-console |
H2 database console |
http://localhost:8080/v3/api-docs |
Raw OpenAPI JSON |
H2 Console settings:
- JDBC URL:
jdbc:h2:mem:tasktrackerdb - Username:
sa - Password: (leave empty)
Spring Security (API gate):
- Username:
admin - Password:
admin123
The app uses Gmail SMTP on port 465 with SSL.
- Go to myaccount.google.com
- Security → 2-Step Verification (must be enabled)
- Scroll down → App Passwords
- Generate one for Mail
- Copy the 16-character password
spring:
mail:
host: smtp.gmail.com
port: 465
username: your-gmail@gmail.com
password: "abcd efgh ijkl mnop" # keep quotes, include spaces
properties:
mail:
smtp:
auth: true
ssl:
enable: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
starttls:
enable: false # must be false when using port 465Important: starttls.enable must be false when using port 465 SSL. Setting both to true causes a connection failure.
The app uses H2 in-memory database. Flyway manages schema creation. Data is seeded automatically via data.sql on every startup.
┌─────────────┐ ┌──────────────┐ ┌────────────────────┐
│ users │ │ projects │ │ tasks │
├─────────────┤ ├──────────────┤ ├────────────────────┤
│ id (PK) │◄──┐ │ id (PK) │◄──┐ │ id (PK) │
│ username │ │ │ name │ │ │ title │
│ email │ │ │ description │ │ │ description │
│ password │ │ │ created_at │ │ │ status (enum) │
│ created_at │ └─────│ owner_id(FK) │ └─────│ project_id (FK) │
└─────────────┘ └──────────────┘ │ priority (enum) │
│ due_date │
┌───────────────────────────────────────────│ assignee_id (FK) │
│ │ created_at │
▼ └────────────────────┘
┌─────────────┐ │
│ users │ │
│ (assignee) │ ┌────────────────────┐ │
└─────────────┘ │ task_activities │ │
├────────────────────┤ │
│ id (PK) │ │
│ action │ │
│ description │ │
│ created_at │ │
│ task_id (FK) │◄──────┘
└────────────────────┘
Enums:
TaskStatus: TODO | IN_PROGRESS | COMPLETED
TaskPriority: LOW | MEDIUM | HIGH
| Entity | Records |
|---|---|
| Users | alice, bob, carol |
| Projects | Website Redesign, Mobile App, Backend API |
| Tasks | 7 tasks across all projects (2 due today) |
| Activities | 9 pre-seeded audit log entries |
Full interactive docs available at: http://localhost:8080/swagger-ui/index.html
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/users |
Create a new user |
| GET | /api/v1/users |
Get all users (paginated) |
| GET | /api/v1/users/{id} |
Get user by ID |
| PUT | /api/v1/users/{id} |
Update user |
| DELETE | /api/v1/users/{id} |
Delete user |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/projects |
Create a new project |
| GET | /api/v1/projects |
Get all projects (paginated) |
| GET | /api/v1/projects/{id} |
Get project by ID |
| PUT | /api/v1/projects/{id} |
Update project |
| DELETE | /api/v1/projects/{id} |
Delete project and all its tasks |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/projects/{projectId}/tasks |
Create a task (owner only) |
| GET | /api/v1/tasks/{id} |
Get task by ID |
| GET | /api/v1/projects/{projectId}/tasks |
Get all tasks in project (filter by ?status=) |
| PUT | /api/v1/tasks/{id} |
Update task (owner: all fields; assignee: status only) |
| DELETE | /api/v1/tasks/{id}?requesterId={userId} |
Delete task (owner only) |
| GET | /api/v1/tasks/due-today |
Get all tasks due today |
| GET | /api/v1/users/{userId}/tasks |
Get all tasks assigned to a user |
| GET | /api/v1/tasks/{taskId}/activities |
Get activity log for a task |
Create User
{
"username": "iva",
"email": "iva@example.com",
"password": "password123"
}Create Project
{
"name": "Website Redesign",
"description": "Redesign the company website",
"ownerId": 1
}Create Task
{
"title": "Build login page",
"description": "Implement login form with validation",
"status": "TODO",
"priority": "HIGH",
"dueDate": "2026-07-01",
"assigneeId": 2,
"requesterId": 1
}Update Task (owner)
{
"title": "Build login page",
"description": "Updated description",
"status": "IN_PROGRESS",
"priority": "HIGH",
"dueDate": "2026-07-05",
"assigneeId": 2,
"requesterId": 1
}Update Task (assignee — only status matters)
{
"title": "Build login page",
"description": "Build login form with validation",
"status": "COMPLETED",
"priority": "HIGH",
"dueDate": "2026-07-01",
"assigneeId": 2,
"requesterId": 2
}| Action | Project Owner | Task Assignee | Other Users |
|---|---|---|---|
| Create project | ✅ | ✅ | ✅ |
| Delete own project | ✅ | ❌ | ❌ |
| Create task in project | ✅ (own projects only) | ❌ | ❌ |
| Edit task (all fields) | ✅ | ❌ | ❌ |
| Edit task (status only) | ✅ | ✅ | ❌ |
| Delete task | ✅ | ❌ | ❌ |
| View all tasks | ✅ | ✅ | ✅ |
| View own assigned tasks | ✅ | ✅ | ✅ |
- Due dates cannot be set in the past (validated in
TaskServiceand blocked in the browser date picker) - Only project owners see the + New Task button on their projects
- Non-owners see a 🔒 lock icon instead of the edit button on tasks they are not assigned to
- The Delete button in the edit modal is hidden for non-owners
- All permission checks are enforced server-side regardless of what the frontend sends
The frontend is a single-page application (SPA) built with plain HTML, CSS, and JavaScript.
| Page | Description |
|---|---|
| Dashboard | Overview with KPI cards (open tasks, due today, projects), quick task lists |
| My Tasks | All tasks assigned to the logged-in user, filterable by status |
| Projects | All projects; owners see a delete button and "You own this" badge |
| Browse Tasks | Browse tasks by project with status filter; + New Task visible only to owners |
| Due Today | All tasks across the system due today |
| People | All registered users; your own card is highlighted |
- Open the app → Register with username, email, password
- On next visit → Sign In with username
- The app uses
admin/admin123as a transparent Spring Security gate — users never see this - Sign Out clears the session
Every task action is recorded:
TASK_CREATED— "Task 'X' created and assigned to Y."TASK_UPDATED— detailed diff e.g. "Status changed from TODO to IN_PROGRESS. Priority changed from LOW to HIGH."TASK_DELETED— recorded before deletion
mvn test| Layer | Test class | What it tests |
|---|---|---|
| Repository | TaskRepositoryTest |
findTasksDueToday() JPQL query via @DataJpaTest |
| Service | UserServiceTest |
User creation, retrieval, update, delete with Mockito mocks |
| Service | ProjectServiceTest |
Project CRUD with mocked repositories |
| Service | TaskServiceTest |
Task creation, update, delete, activity logging with mocks |
| Controller | UserControllerTest |
HTTP endpoints via MockMvc + @WithMockUser |
| Controller | ProjectControllerTest |
HTTP endpoints via MockMvc |
| Controller | TaskControllerTest |
HTTP endpoints via MockMvc |
-
No JWT — Spring Security Basic Auth is used as the API gate. The frontend handles user identity by matching the registered username against the database. This is appropriate for an internal/assignment context but would need JWT for production.
-
Passwords are stored in plain text — For this assignment, passwords are not hashed. A production app would use BCrypt via Spring Security's
PasswordEncoder. -
H2 in-memory — Data does not persist between restarts. This is intentional for development/demo purposes as specified in the assignment.
-
Single project ownership — A project has one owner. There is no concept of co-owners or roles within a project beyond owner/member.
-
Task assignee = team member — Any registered user can be assigned a task. There is no explicit "join project" step.
-
Email is best-effort — If SMTP is not configured or the network blocks the port, the app continues normally and logs a warning. Tasks are always created successfully regardless of email status.
-
requesterId sent from frontend — Since there is no JWT session, the logged-in user's ID is sent in the request body as
requesterIdfor permission checks. This is validated server-side against the actual database record.