freightdesk/webapp/src/views/pages/dashboard.ejs
FreightDesk 1a4eaaa040 Initial commit: FreightDesk v1.0
- Express + EJS server-rendered app
- Supabase PostgreSQL database
- Auth: username/password with bcrypt
- Dashboard with business stats
- Load CRUD with filters
- WhatsApp message parser
- Payment tracking
- Shipper & vehicle management
- Reports (monthly, top shippers, routes)
- Government-app aesthetic (tricolor theme)
- Dark mode support
- Docker + Coolify deployment ready
- Seed data from existing business ledger (88 loads, 41 shippers, 70 vehicles)
2026-06-07 18:57:24 +00:00

132 lines
4.2 KiB
Text

<!-- Dashboard Page -->
<%- include('../partials/header', { activeMenu: 'dashboard' }) %>
<div class="page-header">
<div>
<h1 class="page-title">&#128202; Dashboard</h1>
<p class="page-subtitle">Welcome back, <%= user.username %>! Here's your business overview.</p>
</div>
<a href="/loads/new" class="btn btn-primary">+ New Load</a>
</div>
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card stat-primary">
<div class="stat-icon">&#128176;</div>
<div class="stat-info">
<span class="stat-value"><%= formatINR(stats.totalFreight) %></span>
<span class="stat-label">Total Freight</span>
</div>
</div>
<div class="stat-card stat-success">
<div class="stat-icon">&#9989;</div>
<div class="stat-info">
<span class="stat-value"><%= formatINR(stats.totalCommission) %></span>
<span class="stat-label">Commission Earned</span>
</div>
</div>
<div class="stat-card stat-warning">
<div class="stat-icon">&#9200;</div>
<div class="stat-info">
<span class="stat-value"><%= formatINR(stats.totalPendingShipper) %></span>
<span class="stat-label">Pending Collection</span>
</div>
</div>
<div class="stat-card stat-info">
<div class="stat-icon">&#128666;</div>
<div class="stat-info">
<span class="stat-value"><%= stats.totalLoads %></span>
<span class="stat-label">Total Loads (<%= stats.settledCount %> settled)</span>
</div>
</div>
</div>
<div class="grid-2">
<!-- Recent Loads -->
<div class="card">
<div class="card-header">
<h3 class="card-title">Recent Loads</h3>
<a href="/loads" class="btn btn-sm btn-outline">View All</a>
</div>
<div class="card-body">
<% if (recentLoads.length === 0) { %>
<p class="empty-state">No loads yet. <a href="/loads/new">Add your first load</a></p>
<% } else { %>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Route</th>
<th>Freight</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<% for (const load of recentLoads) { %>
<tr>
<td><%= load.date || '—' %></td>
<td><%= load.from_city || '?' %> &#8594; <%= load.to_city || '?' %></td>
<td><%= formatINR(load.freight_charged) %></td>
<td><span class="badge badge-<%= getStatusColor(load.status) %>"><%= load.status %></span></td>
</tr>
<% } %>
</tbody>
</table>
</div>
<% } %>
</div>
</div>
<!-- Pending Collections -->
<div class="card">
<div class="card-header">
<h3 class="card-title">&#9200; Pending Collections</h3>
</div>
<div class="card-body">
<% if (pendingCollection.length === 0) { %>
<p class="empty-state">No pending collections. Great job!</p>
<% } else { %>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Shipper</th>
<th>Route</th>
<th>Pending</th>
</tr>
</thead>
<tbody>
<% for (const load of pendingCollection) { %>
<tr>
<td><%= load.shipper_id || '—' %></td>
<td><%= load.from_city || '?' %> &#8594; <%= load.to_city || '?' %></td>
<td class="text-danger"><%= formatINR(load.pending_from_shipper) %></td>
</tr>
<% } %>
</tbody>
</table>
</div>
<% } %>
</div>
</div>
</div>
<!-- Status Breakdown -->
<div class="card mt-4">
<div class="card-header">
<h3 class="card-title">Status Breakdown</h3>
</div>
<div class="card-body">
<div class="status-grid">
<% for (const [status, count] of Object.entries(statusCounts)) { %>
<div class="status-item">
<span class="badge badge-<%= getStatusColor(status) %>"><%= count %></span>
<span class="status-label"><%= status %></span>
</div>
<% } %>
</div>
</div>
</div>
<%- include('../partials/footer') %>