<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Command Sovereignty - Dashboard</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/index-Cw0oeWwG.css">
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.js"></script>
<style>
body {
font-family: 'Plus Jakarta Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
background-color: #0a0f1a;
color: #fff;
}
.dash-header {
background: rgba(10, 16, 28, 0.9);
backdrop-filter: blur(16px);
border-bottom: 1px solid rgba(255,255,255,0.1);
padding: 1rem 2rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.dash-logo {
display: flex;
align-items: center;
gap: 0.75rem;
}
.dash-logo-icon {
width: 36px;
height: 36px;
border-radius: 0.5rem;
background: #00A846;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 13px;
color: #fff;
}
.dash-logo-text {
font-size: 1.125rem;
font-weight: 700;
}
.dash-nav {
display: flex;
align-items: center;
gap: 2rem;
}
.dash-nav a {
color: #9ca3af;
text-decoration: none;
font-size: 0.875rem;
transition: color 0.2s;
}
.dash-nav a:hover, .dash-nav a.active {
color: #fff;
}
/* Clickable stat cards */
.stat-card {
cursor: pointer;
transition: border-color 0.2s, transform 0.15s ease, box-shadow 0.15s ease;
}
.stat-card:hover {
border-color: rgba(0,168,70,0.5);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 168, 70, 0.15);
}
.stat-card-link {
text-decoration: none;
color: inherit;
display: block;
}
.dash-user {
display: flex;
align-items: center;
gap: 0.75rem;
}
.dash-user-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: linear-gradient(135deg, #00A846, #00AECB);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 0.75rem;
}
.btn-logout {
background: rgba(255,255,255,0.1);
border: none;
color: #9ca3af;
padding: 0.375rem 0.75rem;
border-radius: 0.5rem;
font-size: 0.75rem;
cursor: pointer;
transition: all 0.2s;
}
.btn-logout:hover {
background: rgba(255,255,255,0.15);
color: #fff;
}
.dash-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.welcome-banner {
background: linear-gradient(135deg, rgba(0,168,70,0.1), rgba(0,174,203,0.1));
border: 1px solid rgba(0,168,70,0.2);
border-radius: 1rem;
padding: 2rem;
margin-bottom: 2rem;
}
.welcome-banner h1 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.welcome-banner p {
color: #9ca3af;
font-size: 0.875rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: rgba(17, 24, 39, 0.8);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 1rem;
padding: 1.5rem;
transition: border-color 0.2s;
}
.stat-card:hover {
border-color: rgba(0,168,70,0.3);
}
.stat-label {
font-size: 0.75rem;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.5rem;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #fff;
}
.stat-change {
font-size: 0.75rem;
margin-top: 0.25rem;
}
.stat-change.positive { color: #00A846; }
.stat-change.negative { color: #ef4444; }
.section-title {
font-size: 1.125rem;
font-weight: 600;
margin-bottom: 1rem;
}
.table-container {
background: rgba(17, 24, 39, 0.8);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 1rem;
overflow: hidden;
}
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th {
background: rgba(255,255,255,0.05);
padding: 0.75rem 1rem;
text-align: left;
font-size: 0.75rem;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
}
.data-table td {
padding: 0.75rem 1rem;
font-size: 0.875rem;
border-top: 1px solid rgba(255,255,255,0.05);
}
.data-table tr:hover td {
background: rgba(255,255,255,0.02);
}
.status-badge {
display: inline-block;
padding: 0.125rem 0.5rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
}
.status-active { background: rgba(0,168,70,0.15); color: #00A846; }
.status-pending { background: rgba(234,179,8,0.15); color: #eab308; }
.status-inactive { background: rgba(107,114,128,0.15); color: #6b7280; }
.loading-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid rgba(255,255,255,0.1);
border-top-color: #00A846;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.auth-loading {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
gap: 1rem;
color: #9ca3af;
}
</style>
</head>
<body class="dark">
<div id="loading" class="auth-loading">
<div class="loading-spinner"></div>
<span>Checking authentication...</span>
</div>
<div id="app" style="display:none;">
<header class="dash-header">
<div class="dash-logo">
<div class="dash-logo-icon">CC</div>
<span class="dash-logo-text">Command Sovereignty</span>
</div>
<nav class="dash-nav">
<a href="#" class="active">Dashboard</a>
<a href="#">Clients</a>
<a href="#">Projects</a>
<a href="#">Analytics</a>
<a href="#">Settings</a>
</nav>
<div class="dash-user">
<div class="dash-user-avatar" id="userAvatar">U</div>
<span id="userName" style="font-size:0.875rem;color:#9ca3af;"></span>
<button class="btn-logout" onclick="handleLogout()">Sign Out</button>
</div>
</header>
<div class="dash-container">
<!-- Welcome Banner -->
<div class="welcome-banner">
<h1 id="welcomeTitle">Welcome back</h1>
<p>Here's your business overview</p>
</div>
<!-- Stats Grid -->
<div class="stats-grid">
<a href="/dashboard/companies" class="stat-card-link">
<div class="stat-card">
<div class="stat-label">Total Clients</div>
<div class="stat-value" id="statClients">-</div>
<div class="stat-change positive" id="statClientsChange">Loading...</div>
</div>
</a>
<a href="/dashboard/projects" class="stat-card-link">
<div class="stat-card">
<div class="stat-label">Active Projects</div>
<div class="stat-value" id="statProjects">-</div>
<div class="stat-change positive" id="statProjectsChange">Loading...</div>
</div>
</a>
<a href="/dashboard/forecast" class="stat-card-link">
<div class="stat-card">
<div class="stat-label">Monthly Revenue</div>
<div class="stat-value" id="statRevenue">-</div>
<div class="stat-change positive" id="statRevenueChange">Loading...</div>
</div>
</a>
<a href="/dashboard/goals" class="stat-card-link">
<div class="stat-card">
<div class="stat-label">Open Opportunities</div>
<div class="stat-value" id="statOpportunities">-</div>
<div class="stat-change positive" id="statOppChange">Loading...</div>
</div>
</a>
</div>
<!-- Recent Activity -->
<h3 class="section-title">Recent Activity</h3>
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>Client</th>
<th>Project</th>
<th>Status</th>
<th>Last Updated</th>
</tr>
</thead>
<tbody id="activityTable">
<tr><td colspan="4" style="text-align:center;color:#6b7280;padding:2rem;">Loading...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
const SUPABASE_URL = 'https://tbomwpzqpbujqhriqzra.supabase.co';
const SUPABASE_ANON_KEY = 'sb_publishable_DTwXPZUigdTML4YhCzoa3w_4awB0i7W';
const supabase = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
async function checkAuth() {
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
// Redirect to login
window.location.href = '/';
return;
}
// User is authenticated
document.getElementById('loading').style.display = 'none';
document.getElementById('app').style.display = 'block';
// Set user info
const email = session.user.email || '';
const name = email.split('@')[0];
document.getElementById('userName').textContent = name;
document.getElementById('userAvatar').textContent = name.charAt(0).toUpperCase();
document.getElementById('welcomeTitle').textContent = `Welcome back, ${name}`;
// Load dashboard data
await loadDashboardData();
// Listen for auth changes
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'signed_out') {
window.location.href = '/';
}
});
}
async function loadDashboardData() {
try {
// Fetch clients count
const { data: clients, error: clientsErr } = await supabase
.from('clients')
.select('*');
if (!clientsErr && clients) {
document.getElementById('statClients').textContent = clients.length;
document.getElementById('statClientsChange').textContent = `${clients.length} total`;
} else {
console.log('Clients fetch:', clientsErr?.message || 'no data');
}
// Fetch projects count
const { data: projects, error: projectsErr } = await supabase
.from('projects')
.select('*');
if (!projectsErr && projects) {
document.getElementById('statProjects').textContent = projects.length;
document.getElementById('statProjectsChange').textContent = `${projects.length} total`;
// Populate activity table
const tbody = document.getElementById('activityTable');
if (projects.length > 0) {
tbody.innerHTML = projects.slice(0, 10).map(p => {
const status = p.status || 'pending';
const statusClass = status === 'active' ? 'status-active' :
status === 'pending' ? 'status-pending' : 'status-inactive';
return `
<tr>
<td>${p.client_name || p.client_id || '-'}</td>
<td>${p.name || p.title || '-'}</td>
<td><span class="status-badge ${statusClass}">${status}</span></td>
<td>${p.updated_at ? new Date(p.updated_at).toLocaleDateString() : '-'}</td>
</tr>
`;
}).join('');
} else {
tbody.innerHTML = '<tr><td colspan="4" style="text-align:center;color:#6b7280;padding:2rem;">No projects yet</td></tr>';
}
} else {
console.log('Projects fetch:', projectsErr?.message || 'no data');
}
// Fetch client spend for revenue
const { data: spend, error: spendErr } = await supabase
.from('client_spend')
.select('*');
if (!spendErr && spend && spend.length > 0) {
const total = spend.reduce((sum, s) => sum + (s.amount || 0), 0);
document.getElementById('statRevenue').textContent = '$' + (total/1000).toFixed(1) + 'K';
} else {
document.getElementById('statRevenue').textContent = '$0';
}
// Set opportunities placeholder
document.getElementById('statOpportunities').textContent = '-';
document.getElementById('statOppChange').textContent = 'Coming soon';
} catch (err) {
console.error('Dashboard load error:', err);
}
}
async function handleLogout() {
await supabase.auth.signOut();
window.location.href = '/';
}
// Init
checkAuth();
</script>
</body>
</html>