Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Coding Challenge Submission - Prabhat Shastri Vemparala #90

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 🚀 Node Modules
node_modules/
frontend/node_modules/
backend/node_modules/

# 🚀 Environment Variables
.env
frontend/.env
backend/.env
.env.local
.env.development.local
.env.test.local
.env.production.local

# 🚀 Logs & Debugging
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# 🚀 Build & Output Files
/build
/dist
/out
frontend/dist/
frontend/build/
frontend/out/
backend/dist/
backend/build/
backend/out/

# 🚀 Database Files
backend/database.sqlite
backend/database.sqlite3
backend/*.db
backend/*.sqlite

# 🚀 Dependency Lock Files (Optional: Keep only one)
package-lock.json

# 🚀 IDE & OS-Specific Files
.vscode/
.idea/
.DS_Store
Thumbs.db

# 🚀 Testing Coverage
/coverage
194 changes: 75 additions & 119 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,119 +1,75 @@
# Full-Stack Coding Challenge

**Deadline**: Sunday, Feb 23th 11:59 pm PST

---

## Overview

Create a “Task Management” application with **React + TypeScript** (frontend), **Node.js** (or **Nest.js**) (backend), and **PostgreSQL** (database). The application should:

1. **Register** (sign up) and **Log in** (sign in) users.
2. After logging in, allow users to:
- **View a list of tasks**.
- **Create a new task**.
- **Update an existing task** (e.g., mark complete, edit).
- **Delete a task**.

Focus on **correctness**, **functionality**, and **code clarity** rather than visual design.
This challenge is intended to be completed within ~3 hours, so keep solutions minimal yet functional.

---

## Requirements

### 1. Authentication

- **User Model**:
- `id`: Primary key
- `username`: Unique string
- `password`: Hashed string
- **Endpoints**:
- `POST /auth/register` – Create a new user
- `POST /auth/login` – Login user, return a token (e.g., JWT)
- **Secure the Tasks Routes**: Only authenticated users can perform task operations.
- **Password Hashing**: Use `bcrypt` or another hashing library to store passwords securely.
- **Token Verification**: Verify the token (JWT) on each request to protected routes.

### 2. Backend (Node.js or Nest.js)

- **Tasks CRUD**:
- `GET /tasks` – Retrieve a list of tasks (optionally filtered by user).
- `POST /tasks` – Create a new task.
- `PUT /tasks/:id` – Update a task (e.g., mark as complete, edit text).
- `DELETE /tasks/:id` – Delete a task.
- **Task Model**:
- `id`: Primary key
- `title`: string
- `description`: string (optional)
- `isComplete`: boolean (default `false`)
- _(Optional)_ `userId` to link tasks to the user who created them
- **Database**: PostgreSQL
- Provide instructions/migrations to set up:
- `users` table (with hashed passwords)
- `tasks` table
- **Setup**:
- `npm install` to install dependencies
- `npm run start` (or `npm run dev`) to run the server
- Document any environment variables (e.g., database connection string, JWT secret)

### 3. Frontend (React + TypeScript)

- **Login / Register**:
- Simple forms for **Register** and **Login**.
- Store JWT (e.g., in `localStorage`) upon successful login.
- If not authenticated, the user should not see the tasks page.
- **Tasks Page**:
- Fetch tasks from `GET /tasks` (including auth token in headers).
- Display the list of tasks.
- Form to create a new task (`POST /tasks`).
- Buttons/fields to update a task (`PUT /tasks/:id`).
- Button to delete a task (`DELETE /tasks/:id`).
- **Navigation**:
- Show `Login`/`Register` if not authenticated.
- Show `Logout` if authenticated.
- **Setup**:
- `npm install` then `npm start` (or `npm run dev`) to run.
- Document how to point the frontend at the backend (e.g., `.env` file, base URL).

---

## Deliverables

1. **Fork the Public Repository**: **Fork** this repo into your own GitHub account.
2. **Implement Your Solution** in the forked repository. Make sure you're README file has:
- Steps to set up the database (migrations, environment variables).
- How to run the backend.
- How to run the frontend.
- Any relevant notes on testing.
- Salary Expectations per month (Mandatory)
3. **Short Video Demo**: Provide a link (in a `.md` file in your forked repo) to a brief screen recording showing:
- Registering a user
- Logging in
- Creating, updating, and deleting tasks
4. **Deadline**: Submissions are due **Sunday, Feb 23th 11:59 pm PST**.

> **Note**: Please keep your solution minimal. The entire project is intended to be completed in around 3 hours. Focus on core features (registration, login, tasks CRUD) rather than polished UI or extra features.

---

## Evaluation Criteria

1. **Functionality**
- Does registration and login work correctly (with password hashing)?
- Are tasks protected by authentication?
- Does the tasks CRUD flow work end-to-end?

2. **Code Quality**
- Is the code structured logically and typed in TypeScript?
- Are variable/function names descriptive?

3. **Clarity**
- Is the `README.md` (in your fork) clear and detailed about setup steps?
- Easy to run and test?

4. **Maintainability**
- Organized logic (controllers/services, etc.)
- Minimal hard-coded values

Good luck, and we look forward to your submission!
Step 1 - Create the database using terminal

createdb -U <postgresusername> task_db

Step 2 - Connect to the database

psql -U <postgresusername> -d task_db

Step 3 - It will take you inside the database (task_db=#) so create the tables by entering :

CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
password TEXT NOT NULL
);

CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
isComplete BOOLEAN DEFAULT FALSE,
userId INTEGER REFERENCES users(id) ON DELETE CASCADE
);

Step 4 - Verify the tables you created

\dt

You should get :
List of relations
Schema | Name | Type | Owner
--------+--------+-------+----------------
public | tasks | table | <postgresusername>
public | users | table | <postgresusername>
(2 rows)


Step 5 - Exit psql

\q

You can connect DBeaver to the database by starting a new connection and connecting it to task_db. It will allow for easy management and checking of the database entries.

Now clone the repository and cd to the repository

Now coming to the setup of the .env files
.env file for backend - It should be in the backend directory

PORT=<port-of-your-choice> (I used 5001)
DATABASE_URL=postgres://<postgresusername>:<youruserpassword>@localhost:<dbport>/task_db
JWT_SECRET=<your-jwt-secret-key>

.env file for the frontend - It should be in the frontend directory

REACT_APP_API_URL=http://localhost:<backend-port> (I used 5001 for the backend so entered that here>

Once the .env files are setup, considering you are in the main repo directory, follow these steps:

1. cd backend
2. npm install
3. To start the backend, enter node src/server.js

4. cd ..
5. cd frontend
6. npm install
7. npm start

Now, the frontend and backend both are running and the database is also connected (Make sure you follow the database connection steps and ensure the postgresql service is active through your terminal).

Pay Expectations (Required to be stated)
As far as the pay is concerned, I am comfortable with the pay stated on the job posting, which is 20-30 per hour. I am doing this to gain further experience and exposure to software engineering and full stack development

Video (Sorry for delay - I realised late that I had not uploaded the video link)
https://drive.google.com/file/d/10f-BETNStuWpGHRZQ_oqNLnp-gI6s_U1/view?usp=sharing
the working flow can be seen here
22 changes: 22 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"bcryptjs": "^3.0.2",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"helmet": "^8.0.0",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"pg": "^8.13.3"
}
}
8 changes: 8 additions & 0 deletions backend/src/config/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { Pool } = require("pg");
require("dotenv").config();

const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});

module.exports = pool;
33 changes: 33 additions & 0 deletions backend/src/constants/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module.exports = {
// Auth related constants
JWT: {
SECRET_KEY: process.env.JWT_SECRET,
EXPIRY: "1h"
},

// HTTP Status codes
HTTP_STATUS: {
OK: 200,
CREATED: 201,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
SERVER_ERROR: 500
},

// Error messages
ERROR_MESSAGES: {
ACCESS_DENIED: "Access Denied",
INVALID_TOKEN: "Invalid Token",
INVALID_CREDENTIALS: "Invalid credentials",
USER_EXISTS: "User already exists",
SERVER_ERROR: "Server error"
},

// Success messages
SUCCESS_MESSAGES: {
TASK_DELETED: "Task deleted"
},

// Crypto related
BCRYPT_SALT_ROUNDS: 10
};
24 changes: 24 additions & 0 deletions backend/src/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const authService = require("../services/authService");
const { HTTP_STATUS, ERROR_MESSAGES } = require("../constants/constants");

const register = async (req, res) => {
const { username, password } = req.body;
try {
const result = await authService.register(username, password);
res.status(HTTP_STATUS.CREATED).json(result);
} catch (error) {
res.status(HTTP_STATUS.SERVER_ERROR).json({ error: ERROR_MESSAGES.USER_EXISTS });
}
};

const login = async (req, res) => {
const { username, password } = req.body;
try {
const result = await authService.login(username, password);
res.json(result);
} catch (error) {
res.status(HTTP_STATUS.BAD_REQUEST).json({ message: error.message });
}
};

module.exports = { register, login };
44 changes: 44 additions & 0 deletions backend/src/controllers/taskController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const taskService = require("../services/taskService");
const { HTTP_STATUS, ERROR_MESSAGES } = require("../constants/constants");

const getTasks = async (req, res) => {
try {
const tasks = await taskService.getTasks(req.user.id);
res.json(tasks);
} catch (error) {
res.status(HTTP_STATUS.SERVER_ERROR).json({ error: ERROR_MESSAGES.SERVER_ERROR });
}
};

const createTask = async (req, res) => {
const { title, description } = req.body;
try {
const task = await taskService.createTask(title, description, req.user.id);
res.json(task);
} catch (error) {
res.status(HTTP_STATUS.SERVER_ERROR).json({ error: ERROR_MESSAGES.SERVER_ERROR });
}
};

const updateTask = async (req, res) => {
const { id } = req.params;
const { title, description, isComplete } = req.body;
try {
const task = await taskService.updateTask(id, title, description, isComplete);
res.json(task);
} catch (error) {
res.status(HTTP_STATUS.SERVER_ERROR).json({ error: ERROR_MESSAGES.SERVER_ERROR });
}
};

const deleteTask = async (req, res) => {
const { id } = req.params;
try {
const result = await taskService.deleteTask(id);
res.json(result);
} catch (error) {
res.status(HTTP_STATUS.SERVER_ERROR).json({ error: ERROR_MESSAGES.SERVER_ERROR });
}
};

module.exports = { getTasks, createTask, updateTask, deleteTask };
Loading