A full-stack time tracking and daily reflection application built with React, Express, and MongoDB. Features local-first caching, JWT authentication, group management with team leads, and Docker containerization for easy deployment.
- Time Tracking: Log work entries with project, category, description, and tags
- Daily Reflections: Record mood, energy, focus levels along with wins, challenges, and learnings
- Week Calendar View: Visual overview of time entries by day
- Reports: Aggregated views of time by project and recent reflections
- Group Management: Organize users into groups with designated team leads
- Team Lead Reports: Group leads can view all entries and reflections from their team members
- Local-First Architecture: Data cached locally with server sync
- User Management: Admin can bulk import users with group assignments and lead status
- CSV Export: Export time entries for external reporting (with optional group filter)
- React 19 with Vite 7
- React Router DOM 7 for navigation
- date-fns for date manipulation
- UUID for client-side ID generation
- ESLint with React hooks plugin
- Express 5 REST API
- MongoDB with Mongoose 9 ODM
- JWT authentication with bcrypt password hashing
- Morgan for request logging
- CORS configured for development
- Docker Compose for local development
- MongoDB 7 container with health checks
- Hot-reload enabled for both frontend and backend
timesheet/
├── apps/
│ └── web/ # React frontend
│ ├── src/
│ │ ├── api/ # API client
│ │ ├── app/ # Route definitions
│ │ ├── components/ # Reusable UI components
│ │ ├── pages/ # Page components
│ │ ├── store/ # State management (Context + hooks)
│ │ └── utils/ # Helper functions
│ └── package.json
├── services/
│ └── api/ # Express backend
│ ├── src/
│ │ ├── middleware/ # Auth middleware
│ │ ├── models/ # Mongoose schemas
│ │ ├── routes/ # API endpoints
│ │ ├── db.js # Database connection
│ │ └── server.js # App entry point
│ └── package.json
├── docker-compose.yml # Container orchestration
└── import_users.py # Bulk user import script
- Docker and Docker Compose
- Node.js 20+ (for local development without Docker)
-
Clone the repository:
git clone <repository-url> cd timesheet
-
Start all services:
docker-compose up
-
Access the application:
- Frontend: http://localhost:5173
- API: http://localhost:8080
- API Health Check: http://localhost:8080/health
-
Start MongoDB locally or use a cloud instance
-
Configure the API:
cd services/api cp .env.example .env # Create and configure .env npm install npm run dev
-
Configure the frontend:
cd apps/web npm install npm run dev
PORT=8080
MONGO_URI=mongodb://localhost:27017/timesheet_reflection
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
JWT_SECRET=your-secret-key-here
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123!ChangeMeVITE_API_URL=http://localhost:8080| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/login |
User login (returns group & isGroupLead) |
| GET | /api/auth/me |
Get current user |
| POST | /api/auth/change-password |
Change password |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/entries |
List entries (with filters) |
| POST | /api/entries |
Create entry |
| PUT | /api/entries/:id |
Update entry |
| DELETE | /api/entries/:id |
Delete entry |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/reflections |
List reflections |
| PUT | /api/reflections/:date |
Upsert reflection by date |
| DELETE | /api/reflections/:id |
Delete reflection |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/settings |
Get user settings |
| PUT | /api/settings |
Update settings |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/group/info |
Get current user's group info |
| GET | /api/group/members |
List group members (lead only) |
| GET | /api/group/entries |
Get group's time entries (lead only) |
| GET | /api/group/reflections |
Get group's reflections (lead only) |
| GET | /api/group/reports/summary |
Get group summary report (lead only) |
| GET | /api/group/reports/user/:userId |
Get detailed report for a member (lead only) |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/admin/import-users |
Bulk import users with group/lead |
| GET | /api/admin/users |
List all users |
| GET | /api/admin/users/:id |
Get single user |
| PUT | /api/admin/users/:id |
Update user (group, isGroupLead, role) |
| DELETE | /api/admin/users/:id |
Delete user and their data |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/groups |
List all groups with member counts |
| GET | /api/admin/groups/:name |
Get group details with members |
| PUT | /api/admin/groups/:name |
Bulk assign users to group |
| DELETE | /api/admin/groups/:name |
Remove all users from group |
| POST | /api/admin/groups/:name/set-lead |
Set/unset group lead |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/admin/export/entries.csv |
Export entries (optional group filter) |
{
username: String,
passwordHash: String,
role: String, // "admin" | "user"
mustChangePassword: Boolean,
group: String, // Group name/identifier (nullable)
isGroupLead: Boolean // Can view group members' reports
}{
userId: ObjectId,
date: String, // yyyy-MM-dd
start: String, // HH:mm
end: String, // HH:mm
project: String,
category: String,
description: String,
tags: [String]
}{
userId: ObjectId,
date: String, // yyyy-MM-dd (unique per user)
mood: Number, // 1-5
energy: Number, // 1-5
focus: Number, // 1-5
wins: String,
challenges: String,
learned: String,
nextSteps: String,
gratitude: String,
notes: String
}{
userId: ObjectId,
projects: [String], // Default: Research, Teaching, Dev, Admin
categories: [String], // Default: Build, Meetings, Writing, Study, Support
reflectionPrompts: [String]
}Use the Python script to import users from a file with group assignments:
# Set environment variables
export API_URL=http://localhost:8080
export ADMIN_USER=admin
export ADMIN_PASS=admin123!ChangeMe
export DEFAULT_PASSWORD=TempPass123!
export INPUT_PATH=users.csv
# Run the import
python import_users.pySimple TXT format (usernames.txt) - one username per line:
user1
user2
user3
CSV format with groups (users.csv):
username,group,isGroupLead
alice,Engineering,true
bob,Engineering,false
carol,Engineering,false
dave,Marketing,true
eve,Marketing,falseThe script will display a summary before importing:
Loaded 5 users from users.csv
Group summary:
Engineering: 3 users (leads: alice)
Marketing: 2 users (leads: dave)
Import result:
Created: 5
Skipped: 0
Errors: 0
You can also import users directly via API:
curl -X POST http://localhost:8080/api/admin/import-users \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"users": [
{"username": "alice", "group": "Engineering", "isGroupLead": true},
{"username": "bob", "group": "Engineering", "isGroupLead": false}
],
"defaultPassword": "TempPass123!",
"mustChangePassword": true
}'Group leads have access to view their team's data:
- View team members:
/api/group/members - View team entries:
/api/group/entries?from=2024-01-01&to=2024-12-31 - View team reflections:
/api/group/reflections - Get summary report:
/api/group/reports/summary - Get individual reports:
/api/group/reports/user/:userId
Example summary response:
{
"group": "Engineering",
"memberCount": 3,
"dateRange": { "from": "2024-01-01", "to": "2024-12-31" },
"timeByUser": [
{ "username": "alice", "totalEntries": 150, "projectCount": 4 },
{ "username": "bob", "totalEntries": 120, "projectCount": 3 }
],
"timeByProject": [
{ "_id": "Development", "entryCount": 200 },
{ "_id": "Meetings", "entryCount": 50 }
],
"reflectionStats": [
{ "username": "alice", "reflectionCount": 30, "avgMood": 4.2, "avgEnergy": 3.8, "avgFocus": 4.0 }
]
}Frontend (apps/web):
npm run dev- Start development servernpm run build- Build for productionnpm run lint- Run ESLintnpm run preview- Preview production build
Backend (services/api):
npm run dev- Start with nodemon (hot reload)npm start- Start production server
# Start all services
docker-compose up
# Start in background
docker-compose up -d
# Stop all services
docker-compose down
# Rebuild containers
docker-compose up --build
# View logs
docker-compose logs -f [service-name]
# Reset database
docker-compose down -v- Local-First: The frontend maintains a local cache (
localStorage) and syncs with the server - Optimistic Updates: UI updates immediately, then syncs with API
- JWT Authentication: Tokens stored in localStorage, included in Authorization header
- First-Login Flow: Users imported by admin must change password on first login
- Group Hierarchy: Users belong to groups, group leads can view their team's data, admins can view everything
ISC