Time-tracking and engagement margin management for professional services teams.
Marginio is a full-stack web application that helps professional services organisations track employee time, manage client engagements, and monitor engagement profitability in real time.
Administrators create clients and engagements (fixed-price or time-and-materials), assign employees with per-assignment billing rates, and define internal cost rates per employee. Employees log daily hours against their assignments through a monthly timesheet workflow with built-in validation rules. Submitted timesheets are reviewed by admins, who can edit, approve, or send them back.
The built-in margin dashboard computes revenue, cost, and margin per engagement automatically, giving managers instant visibility into which projects are profitable and which need attention. Three report views — by user/month, by T&M engagement, and by T&M client — can be exported to Excel (.xlsx) with a single click.
- Client management — create, edit, activate/deactivate client records
- Engagement management — fixed-price and T&M engagements with budget, date range, and code
- User management — create employees and admins, change passwords, toggle active status
- Cost rates — define per-employee hourly cost rates with date ranges
- Assignments — assign users to engagements with billing rates and validated date ranges
- Timesheet management — review submitted timesheets, edit entries, approve or send back to draft
- Reports — user monthly, user/engagement (T&M), user/client (T&M) with Excel export
- Margin dashboard — per-engagement revenue, cost, margin, and margin-per-hour with sortable columns
- Monthly timesheet — pick a month, add/edit/delete time entries against active assignments
- Validation feedback — blocking errors (>= 24 h/day) and non-blocking warnings (> 8 h/day)
- Submit workflow — submit a draft timesheet for admin review; entries become read-only after submission
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Tailwind CSS 3, Vite 6 |
| Backend | Node.js 20, Express 4, TypeScript |
| Database | PostgreSQL 16 |
| ORM | Prisma 5 |
| Auth | JWT (access token 15 min + HTTP-only refresh cookie 7 days) |
| Excel export | exceljs |
| HTTP client | Axios |
| Notifications | react-hot-toast |
| Routing | react-router-dom 6 |
| Infrastructure | Docker Compose (3 services: db, backend, frontend) |
marginio/
├── docker-compose.yml # Orchestrates db, backend, frontend
├── README.md
├── INSTALLING.md # Full installation guide
├── docs/ # Project documentation
│ ├── architecture.md
│ ├── data-model.md
│ ├── api.md
│ ├── features.md
│ ├── roles-and-permissions.md
│ └── development.md
├── backend/
│ ├── Dockerfile
│ ├── package.json
│ ├── tsconfig.json
│ ├── prisma/
│ │ ├── schema.prisma # Database schema
│ │ ├── seed.ts # Seed data script
│ │ └── migrations/ # SQL migration files
│ └── src/
│ ├── server.ts # Express app entry point
│ ├── middleware/ # auth, errorHandler
│ ├── routes/ # auth, clients, engagements, users, costRates,
│ │ # assignments, timesheets, timeEntries, reports, margins
│ └── utils/ # jwt helpers
└── frontend/
├── Dockerfile
├── package.json
├── tsconfig.json
├── vite.config.ts
├── tailwind.config.ts
├── index.html
└── src/
├── main.tsx # React entry point
├── App.tsx # Router + route definitions
├── api/ # Axios client with interceptors
├── auth/ # AuthContext, ProtectedRoute, LoginPage
├── components/ # Layout, Modal, MonthPicker, SearchableSelect, etc.
├── features/
│ ├── admin/ # Clients, Engagements, Users, CostRates, Assignments,
│ │ # AdminTimesheets CRUD pages
│ ├── timesheets/ # Employee timesheet page
│ └── reports/ # UserMonthlyReport, UserEngagementReport,
│ # UserClientReport, MarginsPage
└── types/ # Shared TypeScript interfaces
git clone <repository-url> marginio
cd marginio
docker compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:3001/api |
| PostgreSQL | localhost:5432 (user: marginio, password: marginio) |
The backend container automatically pushes the Prisma schema and seeds the database on first start.
For the full installation guide, see INSTALLING.md.
| Username | Password | Role |
|---|---|---|
admin |
admin123 |
Admin |
john.doe |
password123 |
Employee |
jane.smith |
password123 |
Employee |
Warning: Change all default passwords before deploying to any non-local environment.
| Document | Description |
|---|---|
| INSTALLING.md | Step-by-step installation and first-run guide |
| docs/architecture.md | System architecture and component diagram |
| docs/data-model.md | Database schema reference with relationships |
| docs/api.md | Full REST API reference |
| docs/features.md | Functional description of every feature |
| docs/roles-and-permissions.md | Role-based access control matrix |
| docs/development.md | Local development workflow and conventions |
| Script | Command | Description |
|---|---|---|
npm run dev |
tsx watch src/server.ts |
Start backend in dev mode with hot reload |
npm run build |
tsc |
Compile TypeScript to dist/ |
npm start |
node dist/server.js |
Run compiled production build |
npm run prisma:generate |
prisma generate |
Regenerate Prisma client |
npm run prisma:migrate |
prisma migrate deploy |
Apply pending migrations |
npm run prisma:seed |
tsx prisma/seed.ts |
Seed the database |
| Script | Command | Description |
|---|---|---|
npm run dev |
vite --host 0.0.0.0 |
Start Vite dev server |
npm run build |
tsc -b && vite build |
Type-check and build for production |
npm run preview |
vite preview |
Preview the production build locally |
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | — | PostgreSQL connection string |
JWT_SECRET |
Yes | change-me-in-production |
Secret for signing access tokens |
JWT_REFRESH_SECRET |
Yes | change-me-refresh |
Secret for signing refresh tokens |
PORT |
No | 3001 |
HTTP port for the Express server |
NODE_ENV |
No | — | Set to production to enable secure cookies |
| Variable | Required | Default | Description |
|---|---|---|---|
VITE_API_URL |
No | http://localhost:3001/api |
Backend API base URL |
- Create a feature branch from
develop:git checkout -b feature/my-feature - Make your changes and ensure
tsc --noEmitpasses for bothbackend/andfrontend/ - Write concise commit messages in imperative mood (e.g. "Add client search filter")
- Open a pull request against
developwith a clear description of the change
This project is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See the LICENSE.md file for the full text.
In short — you may use, modify, and distribute this software, but any modified version that is accessible over a network must also make its complete source code available under the same licence.
Copyright (C) 2026 Marginio
Prompting by Luigi Bellio and Development by Claude Code