Coastal Timesheet

Coastal Timesheet v2

A modern, mobile-first time tracking application for Coastal Contracting of FL

React 18 Express PostgreSQL Prisma Docker Tailwind

--- ## ✨ Features - **πŸ“± Mobile-first design** β€” Built for field workers, optimized for phones - **πŸ” JWT authentication** β€” Secure login with access/refresh tokens + rate limiting - **πŸ“… Weekly timesheets** β€” Monday–Sunday pay period with auto-save - **🏠 Multiple homeowners per day** β€” Track work at different job sites - **βœ… Submit β†’ Approve workflow** β€” Employees submit, admins approve or reject - **πŸ“„ PDF generation** β€” Professional server-side PDF export - **πŸ“§ Email integration** β€” Send timesheets via email with SMTP - **πŸ‘₯ Admin panel** β€” Manage employees, homeowners, review timesheets - **πŸ“Š Reporting** β€” Filter by employee, date range, status with summary stats - **πŸŒ™ Dark mode** β€” System-aware with manual toggle - **🐳 One-command deploy** β€” Single `docker compose up` for the entire stack --- ## πŸ“Έ Screenshots
Timesheet Entry Entry Form Dark Mode
Admin Panel Reports & Filters History
### Desktop --- ## πŸš€ Quick Start ### Prerequisites - [Docker](https://docs.docker.com/get-docker/) and Docker Compose - That's it. Everything else runs in containers. ### Deploy ```bash # Clone the repo git clone https://git.bizzle.lol/bizzle/coastal_timesheet.git cd coastal_timesheet git checkout v2 # Configure environment cp .env.example .env # Edit .env with your secrets (see Configuration below) # Launch cd docker docker compose up -d ``` The app will be available at `http://localhost` (port 80). ### Default Admin Account | Field | Value | |----------|-------------------------| | Email | `admin@coastal.com` | | Password | `CoastalAdmin2026!` | > ⚠️ **Change the admin password after first login.** --- ## βš™οΈ Configuration Copy `.env.example` to `.env` and configure: ```env # Database (auto-configured in Docker) DB_PASSWORD=your-secure-db-password # JWT Secrets (CHANGE THESE!) JWT_SECRET=your-jwt-secret-min-32-chars JWT_REFRESH_SECRET=your-refresh-secret-min-32-chars # CORS (add your domain) CORS_ORIGINS=https://timesheets.yourdomain.com # Email (optional β€” for sending timesheets) SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=your-email@gmail.com SMTP_PASS=your-app-password SMTP_FROM=timesheets@yourdomain.com ADMIN_EMAIL=admin@yourdomain.com ``` ### Email Setup (Gmail) 1. Enable 2FA on your Google account 2. Go to [App Passwords](https://myaccount.google.com/apppasswords) 3. Generate a new app password for "Mail" 4. Use that as `SMTP_PASS` --- ## πŸ—οΈ Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Nginx β”‚ β”‚ (reverse proxy) β”‚ β”‚ /api/* β†’ backend:3001 β”‚ β”‚ /* β†’ static frontend β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Frontend β”‚ Backend β”‚ β”‚ React + Vite β”‚ Express + Prisma β”‚ β”‚ Tailwind CSS β”‚ JWT Auth β”‚ β”‚ Lucide Icons β”‚ @react-pdf/renderer β”‚ β”‚ β”‚ Nodemailer β”‚ β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ PostgreSQL 16 β”‚ β”‚ β”‚ (persistent volume) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Tech Stack | Layer | Technology | |-----------|-----------------------------------------------| | Frontend | React 18, Vite 6, Tailwind CSS 3, Lucide | | Backend | Express 4, Prisma ORM, bcryptjs, jsonwebtoken | | Database | PostgreSQL 16 (Alpine) | | PDF | @react-pdf/renderer (server-side) | | Email | Nodemailer + SMTP | | Proxy | Nginx 1.27 (Alpine) | | Container | Docker Compose v3.9 | --- ## πŸ“ Project Structure ``` . β”œβ”€β”€ frontend/ # React SPA β”‚ β”œβ”€β”€ src/ β”‚ β”‚ β”œβ”€β”€ api/ # Axios client with token refresh β”‚ β”‚ β”œβ”€β”€ components/ # Reusable UI components β”‚ β”‚ β”œβ”€β”€ contexts/ # Auth context (JWT) β”‚ β”‚ β”œβ”€β”€ hooks/ # Custom hooks (theme, swipe, auto-save) β”‚ β”‚ └── pages/ # Route pages β”‚ └── vite.config.js β”œβ”€β”€ backend/ # Express API β”‚ β”œβ”€β”€ prisma/ β”‚ β”‚ β”œβ”€β”€ schema.prisma # Database schema β”‚ β”‚ └── seed.js # Seed admin + homeowners β”‚ └── src/ β”‚ β”œβ”€β”€ middleware/ # Auth middleware β”‚ β”œβ”€β”€ routes/ # API routes β”‚ └── utils/ # PDF, email, validation β”œβ”€β”€ docker/ # Deployment β”‚ β”œβ”€β”€ docker-compose.yml β”‚ β”œβ”€β”€ backend/Dockerfile β”‚ β”œβ”€β”€ frontend/Dockerfile β”‚ └── nginx/default.conf └── docs/screenshots/ # App screenshots ``` --- ## πŸ”’ Security - **bcrypt** password hashing (12 rounds) - **JWT** access tokens (15min) + refresh tokens (7 days) - **Helmet** security headers - **Rate limiting** on auth endpoints (50 req / 15 min) - **Zod** input validation on all endpoints - **Prisma ORM** β€” parameterized queries (no SQL injection) - **Non-root Docker** containers - **CORS** origin whitelist --- ## πŸ“‘ API Endpoints ### Auth | Method | Endpoint | Description | |--------|----------------------|----------------------| | POST | `/api/auth/login` | Login, get tokens | | POST | `/api/auth/refresh` | Refresh access token | | GET | `/api/auth/me` | Current user info | ### Entries | Method | Endpoint | Description | |--------|---------------------|------------------------| | GET | `/api/entries` | List entries (by week) | | POST | `/api/entries` | Create entry | | PUT | `/api/entries/:id` | Update entry | | DELETE | `/api/entries/:id` | Delete entry | ### Timesheets | Method | Endpoint | Description | |--------|------------------------------|----------------------| | GET | `/api/timesheets` | Get current week | | GET | `/api/timesheets/history` | All user timesheets | | POST | `/api/timesheets/submit` | Submit for approval | | GET | `/api/timesheets/:id/pdf` | Download PDF | ### Admin | Method | Endpoint | Description | |--------|-------------------------------------|-------------------------| | GET | `/api/admin/timesheets` | All timesheets (filter) | | GET | `/api/admin/timesheets/:id` | Timesheet detail | | PUT | `/api/admin/timesheets/:id/approve` | Approve timesheet | | PUT | `/api/admin/timesheets/:id/reject` | Reject timesheet | | PUT | `/api/admin/timesheets/:id/reopen` | Reopen for editing | | GET | `/api/admin/users` | List employees | | POST | `/api/admin/users` | Create employee | | GET | `/api/admin/homeowners` | List homeowners | | POST | `/api/admin/homeowners` | Add homeowner | | GET | `/api/admin/reports` | Reporting with filters | --- ## πŸ”„ Upgrading from v1 v2 is a complete rewrite. Key differences: | Feature | v1 | v2 | |-----------------|-----------------------------|----------------------------------| | Storage | Browser localStorage | PostgreSQL database | | Auth | None | JWT with roles | | Multi-user | No | Yes β€” unlimited employees | | Approval flow | No | Submit β†’ Approve/Reject | | PDF | Client-side (jsPDF) | Server-side (@react-pdf) | | Email | mailto: link | SMTP with PDF attachment | | Deploy | Static HTML | Docker Compose (one command) | | Admin panel | No | Full admin with reporting | | Dark mode | No | System-aware + manual toggle | --- ## πŸ“ License Private β€” Coastal Contracting of FL. All rights reserved. ---

Built with β˜€οΈ in Florida