A maritime harbor towage dispatch portal built with .NET 8 and Vue 3. Customers request tug assists for vessel arrivals, departures, and shifts. Dispatchers confirm requests, create jobs, assign tugs, and manage the full job lifecycle. Crew members track their assignments. Managers monitor fleet performance through an analytics dashboard.
| URL | |
|---|---|
| Frontend | zealous-coast-0179d5110.6.azurestaticapps.net |
| API Health | merrytime-api.azurewebsites.net/api/health |
The API runs on Azure App Service free tier — first request may take 15-30s to wake up.
| Password | Role | |
|---|---|---|
dispatcher@merrytime.ca |
Dispatch123! |
Dispatcher |
manager@merrytime.ca |
Manager123! |
Manager |
captain@merrytime.ca |
Captain123! |
Crew |
sarah.chen@pacificshipping.ca |
Customer123! |
Customer |
| Layer | Technology |
|---|---|
| Backend | C# / .NET 8 / ASP.NET Core |
| Frontend | Vue 3 + TypeScript + Vite |
| Database | PostgreSQL 16 |
| ORM | Entity Framework Core 8 |
| Real-time | SignalR (WebSocket) |
| Auth | ASP.NET Identity + JWT Bearer |
| Styling | Tailwind CSS v4 + shadcn/vue |
| Charts | Chart.js + vue-chartjs |
| Testing | xUnit + Moq + FluentValidation.TestHelper |
| CI/CD | GitHub Actions → Azure |
| Hosting | Azure App Service + Static Web Apps + PostgreSQL Flexible Server |
Clean architecture with four layers. Dependencies flow inward: Api → Infrastructure → Application → Core.
┌─────────────────────────────────────────────────────────┐
│ Vue 3 + TypeScript │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │ Customer │ │Dispatcher│ │ Crew │ │ Manager │ │
│ │ Portal │ │Dashboard │ │ View │ │ Analytics │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
└────────────────────────┬────────────────────────────────┘
│ REST + SignalR (WebSocket)
┌────────────────────────┴────────────────────────────────┐
│ ASP.NET Core 8 Web API │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │Controllers│ │ Services │ │ Hubs │ │Middleware │ │
│ │ (API) │ │(Business)│ │(SignalR) │ │(Auth/Err) │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
│ ┌──────────────────────────────────────────────────────┐│
│ │ Repository Layer (EF Core + Npgsql) ││
│ └──────────────────────────────────────────────────────┘│
└────────────────────────┬────────────────────────────────┘
│
┌──────────┴──────────┐
│ PostgreSQL 16 │
└─────────────────────┘
| Layer | Responsibility |
|---|---|
| Core | Domain entities, enums, repository interfaces |
| Application | DTOs (immutable records), service interfaces, FluentValidation validators |
| Infrastructure | EF Core DbContext, repository + service implementations |
| Api | Controllers, middleware, SignalR hub, DI wiring |
- Submit towage requests with vessel details, port, berth, and scheduling
- Track request and job status in a unified dashboard
- View job details with assigned tugs and timeline
- Request history for completed/cancelled items
- Unified dispatch view — pending requests and active jobs in one table
- Confirm or cancel incoming requests
- Create jobs from confirmed requests
- Assign tugs with role selection (Lead Tug, Assist Tug, Standby)
- Advance job status through the lifecycle
- Dispatch history for completed jobs
- View assignments filtered to the crew member's vessel
- Start and complete jobs
- Assignment history
- KPI cards: total jobs, completion rate, average duration, active vessels
- Jobs by status breakdown (doughnut chart)
- Jobs by port distribution (bar chart)
- Monthly job trend (line chart)
- Real-time updates via SignalR — all connected clients see changes instantly
- Role-based access control (4 roles with route guards)
- Toast notifications for all user actions
- Loading skeletons during data fetches
- Dark theme using Supabase's design system
- Mobile-responsive layout
- Vessel detail pages with document upload (drag-and-drop)
- Global error handling middleware with structured error responses
Customer submits request
│
▼
Dispatcher confirms request
│
▼
Dispatcher creates job (Scheduled)
│
▼
Dispatcher assigns tug(s) (Dispatched)
│
▼
Crew starts job (InProgress)
│
▼
Crew completes job (Completed)
All transitions broadcast real-time events via SignalR. Every status change is recorded in the audit trail.
merrytime/
├── backend/
│ ├── src/
│ │ ├── MerryTime.Api/ # Controllers, middleware, SignalR hub
│ │ ├── MerryTime.Application/ # DTOs, service interfaces, validators
│ │ ├── MerryTime.Core/ # Entities, enums, repository interfaces
│ │ └── MerryTime.Infrastructure/ # EF Core, repositories, services
│ └── tests/
│ └── MerryTime.UnitTests/ # 90 unit tests
├── frontend/
│ └── src/
│ ├── components/ # shadcn/vue + role-specific dialogs
│ ├── composables/ # useSignalR
│ ├── layouts/ # AppLayout (sidebar shell)
│ ├── lib/ # API client, utilities
│ ├── stores/ # Pinia auth store
│ ├── types/ # TypeScript interfaces
│ └── views/ # Role-based views
├── docker-compose.yml # Full-stack local development
└── .github/workflows/ # CI/CD pipelines
git clone https://github.com/brendonson/merrytime.git
cd merrytime
cp .env.example .env # uses sensible defaults, edit if needed
docker compose up- Frontend: http://localhost:3000
- API + Swagger: http://localhost:5012/swagger
- PostgreSQL: localhost:5433
Prerequisites: .NET 8 SDK, Node.js 22+, pnpm, Docker (for PostgreSQL)
# Start PostgreSQL
docker compose up postgres -d
# Backend
dotnet run --project backend/src/MerryTime.Api
# Frontend (new terminal)
pnpm --prefix frontend install
pnpm --prefix frontend dev- Frontend: http://localhost:5173
- API + Swagger: http://localhost:5012/swagger
The database is automatically migrated and seeded on first startup with 5 Canadian ports, 10 tugs, 3 customers, 8 users, and 10 jobs across all lifecycle statuses.
dotnet test backend/MerryTime.sln90 unit tests covering:
- Service layer — job lifecycle, request handling, vessel management, document uploads
- Validators — all 8 FluentValidation validators with happy/unhappy paths
- Naming convention —
MethodName_Scenario_ExpectedResult
- Repository + Unit of Work — generic
IRepository<T>with specialized repositories, mutations throughIUnitOfWork.SaveChangesAsync() - Clean Architecture — strict layer separation, dependencies flow inward only
- SignalR Real-time —
IDispatchNotifierabstraction decouples broadcast logic from business services - Immutable DTOs — all data transfer objects are C#
recordtypes - FluentValidation — validators in Application layer with per-request evaluation
- Job State Machine —
Scheduled → Dispatched → InProgress → Completed → Invoicedwith audit trail - Role-Based Access — JWT claims (
CustomerId,VesselId) scope data per user - Global Exception Handling — middleware maps domain exceptions to structured HTTP responses