Skip to content
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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ StudyMatePlus/
- npm 9 or newer
- MongoDB connection string

## Docker Setup (Alternative & Recommended)

If you have Docker and Docker Compose installed, you can spin up the complete development environment (Frontend, Backend, and a local MongoDB instance) with a single command. This avoids having to configure local environments manually.

### 1. Build and start containers

```bash
docker-compose up --build
```

### 2. Access the application

- **Frontend client**: http://localhost:3000
- **Backend API**: http://localhost:5000

The services support volume mounting, so any changes you make in `client/` or `server/` will automatically sync and trigger hot reloading inside the containers.

## Local setup

### 1. Clone the repository
Expand Down
6 changes: 6 additions & 0 deletions client/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
build
npm-debug.log
.env
.git
.gitignore
19 changes: 19 additions & 0 deletions client/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM node:20-alpine

# Set working directory
WORKDIR /app

# Copy package files first
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application files
COPY . .

# Expose react port
EXPOSE 3000

# Start client server
CMD ["npm", "start"]
19 changes: 17 additions & 2 deletions client/src/pages/Todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function Todo() {
const [form, setForm] = useState(defaultForm);
const [isEditing, setIsEditing] = useState(false);
const [showModal, setShowModal] = useState(false);
const [error, setError] = useState("");

useEffect(() => {
// Load tasks and normalize recurring tasks so their deadlines are not in the past
Expand Down Expand Up @@ -77,11 +78,20 @@ export default function Todo() {
setForm(defaultForm);
setIsEditing(false);
setShowModal(false);
setError("");
}

function handleSubmit(e) {
e.preventDefault();
if (!form.title || !form.deadline) return alert("Please provide a title and deadline");
if (!form.title || !form.title.trim()) {
setError("Task title is required");
return;
}
if (!form.deadline) {
setError("Deadline is required");
return;
}
setError("");

if (isEditing) {
setTasks((prev) => prev.map((t) => (t.id === form.id ? { ...t, ...form } : t)));
Expand Down Expand Up @@ -172,10 +182,14 @@ export default function Todo() {
<button aria-label="Close" className="modal-close" onClick={resetForm}>×</button>
</div>
<form className="todo-form" onSubmit={handleSubmit}>
{error && <div className="error-message" style={{color: 'red', fontSize: '0.85rem', marginBottom: '0.5rem'}}>{error}</div>}
<div className="row">
<input
value={form.title}
onChange={(e) => setForm((f) => ({ ...f, title: e.target.value }))}
onChange={(e) => {
setForm((f) => ({ ...f, title: e.target.value }));
if (error) setError("");
}}
placeholder="Task title"
/>
<select value={form.recurrence} onChange={(e) => setForm((f) => ({ ...f, recurrence: e.target.value }))}>
Expand All @@ -202,6 +216,7 @@ export default function Todo() {
const v = e.target.value; // yyyy-mm-ddThh:mm
const iso = v ? new Date(v).toISOString() : '';
setForm((f) => ({ ...f, deadline: iso }));
if (error) setError("");
}}
/>
</label>
Expand Down
57 changes: 57 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
services:
mongodb:
image: mongo:latest
container_name: studymateplus-db
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
networks:
- studymateplus-network

server:
build:
context: ./server
dockerfile: Dockerfile
container_name: studymateplus-server
ports:
- "5000:5000"
environment:
- PORT=5000
- MONGO_URI=mongodb://mongodb:27017/studymateplus
- JWT_SECRET=studymateplus_secret_key_123
- CLIENT_URL=http://localhost:3000
volumes:
- ./server:/app
- /app/node_modules
depends_on:
- mongodb
networks:
- studymateplus-network

client:
build:
context: ./client
dockerfile: Dockerfile
container_name: studymateplus-client
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:5000
- WDS_SOCKET_PORT=0
stdin_open: true
tty: true
volumes:
- ./client:/app
- /app/node_modules
depends_on:
- server
networks:
- studymateplus-network

volumes:
mongo-data:

networks:
studymateplus-network:
driver: bridge
5 changes: 5 additions & 0 deletions server/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
npm-debug.log
.env
.git
.gitignore
19 changes: 19 additions & 0 deletions server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM node:20-alpine

# Set working directory
WORKDIR /app

# Copy package files first
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application files
COPY . .

# Expose server port
EXPOSE 5000

# Start server in dev mode
CMD ["npm", "run", "dev"]